]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSMacOSX/dnssd_svcb.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / dnssd_svcb.c
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
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
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 *
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 *
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 *
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
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
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
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 *
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 *
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
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
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 }