1 /*
2  * Copyright (c) 2020 Apple Inc. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "dnssd_svcb.h"
18 
19 #include <netinet/in.h>
20 
21 typedef enum
22 {
23     dnssd_svcb_key_mandatory = 0,
24     dnssd_svcb_key_alpn = 1,
25     dnssd_svcb_key_no_default_alpn = 2,
26     dnssd_svcb_key_port = 3,
27     dnssd_svcb_key_ipv4_hint = 4,
28     dnssd_svcb_key_ech_config = 5,
29     dnssd_svcb_key_ipv6_hint = 6,
30     dnssd_svcb_key_doh_uri = 32768,
31 } dnssd_svcb_key_t;
32 
33 typedef bool (^_dnssd_svcb_access_value_block_t)(const void *value, size_t value_size);
34 
35 uint16_t
dnssd_svcb_get_priority(const uint8_t * buffer,size_t buffer_size)36 dnssd_svcb_get_priority(const uint8_t *buffer, size_t buffer_size)
37 {
38 	if (buffer_size < sizeof(uint16_t)) {
39 		return 0;
40 	}
41 
42 	const uint16_t *priority_p = (const uint16_t *)buffer;
43 	return (uint16_t)htons(*priority_p);
44 }
45 
46 #define DNSSD_MAX_DOMAIN_NAME 256
47 #define DNSSD_MAX_DOMAIN_LABEL 63
48 #define DNSSD_MAX_ESCAPED_DOMAIN_NAME 1009
49 
50 static bool
_dnssd_svcb_get_domain_name_length(const uint8_t * buffer,size_t buffer_size,size_t * out_name_length)51 _dnssd_svcb_get_domain_name_length(const uint8_t *buffer, size_t buffer_size, size_t *out_name_length)
52 {
53 	const uint8_t *limit = buffer + buffer_size;
54 	const uint8_t *cursor = buffer;
55     while (cursor != NULL && cursor < limit) {
56 		if (*cursor == 0) {
57 			*out_name_length = ((uint16_t)(cursor - buffer + 1));
58 			if (*out_name_length > DNSSD_MAX_DOMAIN_NAME) {
59 				return false;
60 			}
61 			return true;
62 		}
63         cursor += 1 + *cursor;
64     }
65 	return false;
66 }
67 
68 static char *
_dnssd_svcb_convert_label_to_string(const uint8_t * source,char * string_buffer)69 _dnssd_svcb_convert_label_to_string(const uint8_t *source, char *string_buffer)
70 {
71     const uint8_t length = *source++; // Read length of this (non-null) label
72     const uint8_t *limit = source + length; // Work out where the label ends
73 	if (length > DNSSD_MAX_DOMAIN_LABEL) {
74 		return NULL;
75 	}
76 	while (source < limit) {
77         uint8_t character = *source++;
78 		if (character == '.' || character == '\\') { // If character is a dot or the escape character
79 			*string_buffer++ = '\\'; // Output escape character
80 		} else if (character <= ' ') { // Output decimal escape sequence
81 			*string_buffer++ = '\\';
82 			*string_buffer++ = (char)  ('0' + (character / 100));
83 			*string_buffer++ = (char)  ('0' + (character / 10) % 10);
84 			character = (uint8_t)('0' + (character) % 10);
85 		}
86         *string_buffer++ = (char)character; // Copy the character
87     }
88     *string_buffer = 0; // Null-terminate the string
89     return(string_buffer); // and return
90 }
91 
92 static char *
_dnssd_svcb_get_string_from_domain_name(const uint8_t * source,char * string_buffer)93 _dnssd_svcb_get_string_from_domain_name(const uint8_t *source, char *string_buffer)
94 {
95     const uint8_t *limit = source + DNSSD_MAX_DOMAIN_NAME;
96 
97 	if (*source == 0) {
98 		*string_buffer++ = '.'; // Special case: For root, just write a dot
99 	}
100 
101     while (*source) {
102 		if (source + 1 + *source >= limit) {
103 			return NULL;
104 		}
105         string_buffer = _dnssd_svcb_convert_label_to_string(source, string_buffer);
106 		if (string_buffer == NULL) {
107 			return NULL;
108 		}
109         source += 1 + *source;
110         *string_buffer++ = '.'; // Write the dot after the label
111     }
112 
113     *string_buffer++ = 0; // Null-terminate the string
114     return string_buffer; // and return
115 }
116 
117 char *
dnssd_svcb_copy_domain(const uint8_t * buffer,size_t buffer_size)118 dnssd_svcb_copy_domain(const uint8_t *buffer, size_t buffer_size)
119 {
120 	if (buffer_size < sizeof(uint16_t)) {
121 		return NULL;
122 	}
123 
124 	buffer += sizeof(uint16_t);
125 	buffer_size -= sizeof(uint16_t);
126 
127 	size_t domain_length = 0;
128 	if (!_dnssd_svcb_get_domain_name_length(buffer, buffer_size, &domain_length)) {
129 		return NULL;
130 	}
131 
132 	char *name_str = calloc(1, DNSSD_MAX_ESCAPED_DOMAIN_NAME);
133 	if (_dnssd_svcb_get_string_from_domain_name(buffer, name_str) == NULL) {
134 		free(name_str);
135 		return NULL;
136 	}
137 	return name_str;
138 }
139 
140 static bool
_dnssd_svcb_extract_values(const uint8_t * buffer,size_t buffer_size,dnssd_svcb_key_t match_key,_dnssd_svcb_access_value_block_t value_block)141 _dnssd_svcb_extract_values(const uint8_t *buffer, size_t buffer_size,
142 						   dnssd_svcb_key_t match_key, _dnssd_svcb_access_value_block_t value_block)
143 {
144 	if (buffer_size < sizeof(uint16_t)) {
145 		return false;
146 	}
147 
148 	const uint16_t *priority_p = (const uint16_t *)buffer;
149 	uint16_t priority = (uint16_t)htons(*priority_p);
150 	if (priority == 0) {
151 		// Alias form, no value
152 		return false;
153 	}
154 
155 	buffer += sizeof(uint16_t);
156 	buffer_size -= sizeof(uint16_t);
157 
158 	size_t domain_length = 0;
159 	if (!_dnssd_svcb_get_domain_name_length(buffer, buffer_size, &domain_length)) {
160 		return false;
161 	}
162 
163 	buffer += domain_length;
164 	buffer_size -= domain_length;
165 
166 	while (buffer != NULL && buffer_size >= (sizeof(uint16_t) + sizeof(uint16_t))) {
167 		const uint16_t *param_key_p = (const uint16_t *)buffer;
168 		uint16_t param_key = (uint16_t)htons(*param_key_p);
169 
170 		buffer += sizeof(uint16_t);
171 		buffer_size -= sizeof(uint16_t);
172 
173 		const uint16_t *param_value_length_p = (const uint16_t *)buffer;
174 		uint16_t param_value_length = (uint16_t)htons(*param_value_length_p);
175 
176 		buffer += sizeof(uint16_t);
177 		buffer_size -= sizeof(uint16_t);
178 
179 		if (param_value_length > buffer_size) {
180 			break;
181 		}
182 
183 		if (match_key == param_key) {
184 			bool continue_looping = value_block(buffer, param_value_length);
185 			if (!continue_looping) {
186 				break;
187 			}
188 		}
189 
190 		buffer += param_value_length;
191 		buffer_size -= param_value_length;
192 	}
193 
194 	return true;
195 }
196 
197 bool
dnssd_svcb_is_valid(const uint8_t * buffer,size_t buffer_size)198 dnssd_svcb_is_valid(const uint8_t *buffer, size_t buffer_size)
199 {
200 	if (buffer_size < sizeof(uint16_t)) {
201 		return false;
202 	}
203 
204 	uint16_t priority = dnssd_svcb_get_priority(buffer, buffer_size);
205 	if (priority == 0) {
206 		// Alias forms don't need further validation
207 		return true;
208 	}
209 
210 	__block bool invalid_mandatory_value = false;
211 	(void)_dnssd_svcb_extract_values(buffer, buffer_size, dnssd_svcb_key_mandatory, ^bool(const void *value, size_t value_size) {
212 		if (value != NULL && value_size > 0) {
213 			if ((value_size % sizeof(uint16_t)) != 0) {
214 				// Value must be a list of keys, as 16-bit integers
215 				invalid_mandatory_value = true;
216 			} else {
217 				const uint16_t mandatory_key_count = (uint16_t)(value_size / sizeof(uint16_t));
218 				for (uint16_t i = 0; i < mandatory_key_count && !invalid_mandatory_value; i++) {
219 					const uint16_t *param_key_p = ((const uint16_t *)value) + i;
220 					uint16_t param_key = (uint16_t)htons(*param_key_p);
221 					switch (param_key) {
222 						case dnssd_svcb_key_mandatory:
223 							// Mandatory key cannot be listed
224 							invalid_mandatory_value = true;
225 							break;
226 						case dnssd_svcb_key_alpn:
227 						case dnssd_svcb_key_no_default_alpn:
228 						case dnssd_svcb_key_port:
229 						case dnssd_svcb_key_ipv4_hint:
230 						case dnssd_svcb_key_ech_config:
231 						case dnssd_svcb_key_ipv6_hint:
232 						case dnssd_svcb_key_doh_uri:
233 							// Known keys are fine
234 							break;
235 						default:
236 							// Unknown mandatory key means we should ignore the record
237 							invalid_mandatory_value = true;
238 							break;
239 					}
240 				}
241 			}
242 		}
243 		return false;
244 	});
245 	if (invalid_mandatory_value) {
246 		return false;
247 	} else {
248 		return true;
249 	}
250 }
251 
252 uint16_t
dnssd_svcb_get_port(const uint8_t * buffer,size_t buffer_size)253 dnssd_svcb_get_port(const uint8_t *buffer, size_t buffer_size)
254 {
255 	__block uint16_t port = false;
256 	(void)_dnssd_svcb_extract_values(buffer, buffer_size, dnssd_svcb_key_port, ^bool(const void *value, size_t value_size) {
257 		if (value != NULL && value_size == sizeof(uint16_t)) {
258 			port = (uint16_t)htons(*(const uint16_t *)value);
259 		}
260 		return false;
261 	});
262 	return port;
263 }
264 
265 char *
dnssd_svcb_copy_doh_uri(const uint8_t * buffer,size_t buffer_size)266 dnssd_svcb_copy_doh_uri(const uint8_t *buffer, size_t buffer_size)
267 {
268 	__block char *doh_uri = NULL;
269 	(void)_dnssd_svcb_extract_values(buffer, buffer_size, dnssd_svcb_key_doh_uri, ^bool(const void *value, size_t value_size) {
270 		if (value != NULL && value_size > 0) {
271 			asprintf(&doh_uri, "%.*s", (int)value_size, value);
272 		}
273 		return false;
274 	});
275 	return doh_uri;
276 }
277 
278 uint8_t *
dnssd_svcb_copy_ech_config(const uint8_t * buffer,size_t buffer_size,size_t * out_length)279 dnssd_svcb_copy_ech_config(const uint8_t *buffer, size_t buffer_size, size_t *out_length)
280 {
281 	__block uint8_t *ech_config = NULL;
282 	(void)_dnssd_svcb_extract_values(buffer, buffer_size, dnssd_svcb_key_ech_config, ^bool(const void *value, size_t value_size) {
283 		if (value != NULL && value_size > 0) {
284 			ech_config = calloc(1, value_size);
285 			*out_length = value_size;
286 			memcpy(ech_config, value, value_size);
287 		}
288 		return false;
289 	});
290 	return ech_config;
291 }
292 
293 void
dnssd_svcb_access_alpn_values(const uint8_t * buffer,size_t buffer_size,DNSSD_NOESCAPE _dnssd_svcb_access_alpn_t block)294 dnssd_svcb_access_alpn_values(const uint8_t *buffer, size_t buffer_size,
295 							  DNSSD_NOESCAPE _dnssd_svcb_access_alpn_t block)
296 {
297 	(void)_dnssd_svcb_extract_values(buffer, buffer_size, dnssd_svcb_key_alpn, ^bool(const void *value, size_t value_size) {
298 		if (value != NULL) {
299 			size_t value_read = 0;
300 			while (value_size > 0 && value_read < value_size) {
301 				char alpn_value[UINT8_MAX] = "";
302 
303 				uint8_t alpn_length = *(const uint8_t *)value;
304 				value_read++;
305 
306 				if (value_read + alpn_length > value_size) {
307 					break;
308 				}
309 
310 				memcpy(alpn_value, ((const uint8_t *)value) + value_read, alpn_length);
311 				if (!block((const char *)alpn_value)) {
312 					break;
313 				}
314 				value_read += alpn_length;
315 			}
316 		}
317 		return false;
318 	});
319 }
320 
321 void
dnssd_svcb_access_address_hints(const uint8_t * buffer,size_t buffer_size,DNSSD_NOESCAPE _dnssd_svcb_access_address_t block)322 dnssd_svcb_access_address_hints(const uint8_t *buffer, size_t buffer_size, DNSSD_NOESCAPE _dnssd_svcb_access_address_t block)
323 {
324 	__block bool continue_enumerating = true;
325 	(void)_dnssd_svcb_extract_values(buffer, buffer_size, dnssd_svcb_key_ipv4_hint, ^bool(const void *value, size_t value_size) {
326 		if (value != NULL && (value_size % sizeof(struct in_addr)) == 0) {
327 			size_t value_read = 0;
328 			while (value_read < value_size) {
329 				struct sockaddr_in v4addr;
330 				memset(&v4addr, 0, sizeof(v4addr));
331 				v4addr.sin_family = AF_INET;
332 				v4addr.sin_len = sizeof(v4addr);
333 				memcpy(&v4addr.sin_addr, ((const uint8_t *)value) + value_read, sizeof(struct in_addr));
334 				continue_enumerating = block((const struct sockaddr *)&v4addr);
335 				if (!continue_enumerating) {
336 					break;
337 				}
338 				value_read += sizeof(struct in_addr);
339 			}
340 		}
341 		return false;
342 	});
343 	if (!continue_enumerating) {
344 		return;
345 	}
346 	(void)_dnssd_svcb_extract_values(buffer, buffer_size, dnssd_svcb_key_ipv6_hint, ^bool(const void *value, size_t value_size) {
347 
348 		if (value != NULL && (value_size % sizeof(struct in6_addr)) == 0) {
349 			size_t value_read = 0;
350 			while (value_read < value_size) {
351 				struct sockaddr_in6 v6addr;
352 				memset(&v6addr, 0, sizeof(v6addr));
353 				v6addr.sin6_family = AF_INET6;
354 				v6addr.sin6_len = sizeof(v6addr);
355 				memcpy(&v6addr.sin6_addr, ((const uint8_t *)value) + value_read, sizeof(struct in6_addr));
356 				continue_enumerating = block((const struct sockaddr *)&v6addr);
357 				if (!continue_enumerating) {
358 					break;
359 				}
360 				value_read += sizeof(struct in6_addr);
361 			}
362 		}
363 		return false;
364 	});
365 }
366