]> git.saurik.com Git - apple/mdnsresponder.git/blob - ServiceRegistration/wireutils.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / ServiceRegistration / wireutils.c
1 /* wireutils.c
2 *
3 * Copyright (c) 2019 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * DNS wire-format utility functions.
18 *
19 * Functions that are neither necessary for very simple DNS packet generation, nor required for parsing
20 * a message, e.g. compression, name printing, etc.
21 */
22
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <sys/socket.h>
28 #include <arpa/inet.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include "srp.h"
32 #include "dns-msg.h"
33
34 #include "mDNSEmbeddedAPI.h"
35 #include "DNSCommon.h"
36
37 #undef LogMsg
38 #define LogMsg(...)
39
40 // We need the compression routines from DNSCommon.c, but we can't link to it because that
41 // pulls in a _lot_ of stuff we don't want. The solution? Define STANDALONE (this is done
42 // in the Makefile, and include DNSCommon.c.
43 //
44 // The only functions that aren't excluded by STANDALONE are FindCompressionPointer and
45 // putDomainNameAsLabels.
46
47 #define STANDALONE
48 #include "../mDNSCore/DNSCommon.c"
49
50 void dns_name_free(dns_label_t *name)
51 {
52 dns_label_t *next;
53 if (name == NULL) {
54 return;
55 }
56 next = name->next;
57 free(name);
58 if (next != NULL) {
59 return dns_name_free(next);
60 }
61 }
62
63 dns_name_t *
64 dns_name_copy(dns_name_t *original)
65 {
66 dns_name_t *ret = NULL, **cur = &ret;
67 dns_name_t *next;
68
69 for (next = original; next; next = next->next) {
70 *cur = calloc(1, 1 + next->len + (sizeof (dns_name_t)) - DNS_MAX_LABEL_SIZE);
71 if (*cur == NULL) {
72 if (ret != NULL) {
73 dns_name_free(ret);
74 }
75 return NULL;
76 }
77 if (next->len) {
78 memcpy((*cur)->data, next->data, next->len + 1);
79 }
80 (*cur)->len = next->len;
81 cur = &((*cur)->next);
82 }
83 return ret;
84 }
85
86 // Needed for TSIG (RFC2845).
87 void
88 dns_u48_to_wire_(dns_towire_state_t *NONNULL txn,
89 uint64_t val, int line)
90 {
91 if (!txn->error) {
92 if (txn->p + 6 >= txn->lim) {
93 txn->error = ENOBUFS;
94 txn->truncated = true;
95 txn->line = line;
96 return;
97 }
98 *txn->p++ = (val >> 40) & 0xff;
99 *txn->p++ = (val >> 32) & 0xff;
100 *txn->p++ = (val >> 24) & 0xff;
101 *txn->p++ = (val >> 16) & 0xff;
102 *txn->p++ = (val >> 8) & 0xff;
103 *txn->p++ = val & 0xff;
104 }
105 }
106
107 void
108 dns_concatenate_name_to_wire_(dns_towire_state_t *towire, dns_name_t *labels_prefix, const char *prefix,
109 const char *suffix, int line)
110 {
111 dns_wire_t namebuf;
112 dns_towire_state_t namewire;
113 mDNSu8 *ret;
114 namebuf.data[0] = 0;
115
116 // Don't do all this work if we're already past an error.
117 if (towire->error) {
118 return;
119 }
120 memset(&namewire, 0, sizeof namewire);
121 namewire.message = &namebuf;
122 namewire.lim = &namebuf.data[DNS_DATA_SIZE];
123 namewire.p = namebuf.data;
124 if (prefix != NULL) {
125 dns_name_to_wire(NULL, &namewire, prefix);
126 } else if (labels_prefix != NULL) {
127 size_t bytes_written;
128
129 if (!namewire.error) {
130 bytes_written = namewire.lim - namewire.p;
131 if (bytes_written > INT16_MAX) {
132 towire->error = true;
133 towire->line = __LINE__;
134 return;
135 }
136 bytes_written = dns_name_to_wire_canonical(namewire.p, (int)bytes_written, labels_prefix);
137 // This can never occur with a valid name.
138 if (bytes_written == 0) {
139 namewire.truncated = true;
140 } else {
141 namewire.p += bytes_written;
142 }
143 }
144 }
145 if (suffix != NULL) {
146 dns_full_name_to_wire(NULL, &namewire, suffix);
147 }
148 if (namewire.error) {
149 towire->truncated = namewire.truncated;
150 towire->error = namewire.error;
151 towire->line = line;
152 }
153
154 ret = putDomainNameAsLabels((DNSMessage *)towire->message, towire->p, towire->lim, (domainname *)namebuf.data);
155 if (ret == NULL) {
156 towire->error = ENOBUFS;
157 towire->truncated = true;
158 towire->line = line;
159 return;
160 }
161
162 // Shouldn't happen
163 if (ret > towire->lim) {
164 towire->error = ENOBUFS;
165 towire->truncated = true;
166 towire->line = line;
167 } else {
168 towire->p = ret;
169 }
170 }
171
172 // Convert a dns_name_t to presentation format. Stop conversion at the specified limit.
173 // A trailing dot is only written if a null label is present.
174 const char *NONNULL
175 dns_name_print_to_limit(dns_name_t *NONNULL name, dns_name_t *NULLABLE limit, char *buf, int bufmax)
176 {
177 dns_label_t *lp;
178 int ix = 0;
179 int i;
180
181 // Copy the labels in one at a time, putting a dot between each one; if there isn't room
182 // in the buffer (shouldn't be the case), copy as much as will fit, leaving room for a NUL
183 // termination.
184 for (lp = name; lp != limit && lp != NULL; lp = lp->next) {
185 if (ix != 0) {
186 if (ix + 2 >= bufmax) {
187 break;
188 }
189 buf[ix++] = '.';
190 }
191 for (i = 0; i < lp->len; i++) {
192 if (isascii(lp->data[i]) && (lp->data[i] == ' ' || isprint(lp->data[i]))) {
193 if (ix + 2 >= bufmax) {
194 break;
195 }
196 buf[ix++] = lp->data[i];
197 } else {
198 if (ix + 5 >= bufmax) {
199 break;
200 }
201 buf[ix++] = '\\';
202 buf[ix++] = '0' + (lp->data[i] / 100);
203 buf[ix++] = '0' + (lp->data[i] / 10) % 10;
204 buf[ix++] = '0' + lp->data[i] % 10;
205 }
206 }
207 if (i != lp->len) {
208 break;
209 }
210 }
211 buf[ix++] = 0;
212 return buf;
213 }
214
215 const char *NONNULL
216 dns_name_print(dns_name_t *NONNULL name, char *buf, int bufmax)
217 {
218 return dns_name_print_to_limit(name, NULL, buf, bufmax);
219 }
220
221 bool
222 dns_labels_equal(const char *label1, const char *label2, size_t len)
223 {
224 unsigned i;
225 for (i = 0; i < len; i++) {
226 if (isascii(label1[i]) && isascii(label2[i])) {
227 if (tolower(label1[i]) != tolower(label2[i])) {
228 return false;
229 }
230 }
231 else {
232 if (label1[i] != label2[i]) {
233 return false;
234 }
235 }
236 }
237 return true;
238 }
239
240 bool
241 dns_names_equal(dns_label_t *NONNULL name1, dns_label_t *NONNULL name2)
242 {
243 if (name1->len != name2->len) {
244 return false;
245 }
246 if (name1->len != 0 && !dns_labels_equal(name1->data, name2->data, name1->len) != 0) {
247 return false;
248 }
249 if (name1->next != NULL && name2->next != NULL) {
250 return dns_names_equal(name1->next, name2->next);
251 }
252 if (name1->next == NULL && name2->next == NULL) {
253 return true;
254 }
255 return false;
256 }
257
258 // Note that "foo.arpa" is not the same as "foo.arpa."
259 bool
260 dns_names_equal_text(dns_label_t *NONNULL name1, const char *NONNULL name2)
261 {
262 const char *ndot;
263 const char *s, *t;
264 int tlen = 0;
265 ndot = strchr(name2, '.');
266 if (ndot == NULL) {
267 ndot = name2 + strlen(name2);
268 }
269 for (s = name2; s < ndot; s++) {
270 if (*s == '\\') {
271 if (s + 4 <= ndot) {
272 tlen++;
273 s += 3;
274 } else {
275 return false; // An invalid name can't be equal to anything.
276 }
277 } else {
278 tlen++;
279 }
280 }
281 if (name1->len != tlen) {
282 return false;
283 }
284 if (name1->len != 0) {
285 t = name1->data;
286 for (s = name2; s < ndot; s++, t++) {
287 if (*s == '\\') { // already bounds checked
288 int v0 = s[1] - '0';
289 int v1 = s[2] - '0';
290 int v2 = s[3] - '0';
291 int val = v0 * 100 + v1 * 10 + v2;
292 if (val > 255) {
293 return false;
294 } else if (isascii(*s) && isascii(*t)) {
295 if (tolower(*s) != tolower(*t)) {
296 return false;
297 }
298 } else if (val != *t) {
299 return false;
300 }
301 s += 3;
302 } else {
303 if (*s != *t) {
304 return false;
305 }
306 }
307 }
308 }
309 if (name1->next != NULL && *ndot == '.') {
310 return dns_names_equal_text(name1->next, ndot + 1);
311 }
312 if (name1->next == NULL && *ndot == 0) {
313 return true;
314 }
315 return false;
316 }
317
318 // Find the length of a name in uncompressed wire format.
319 static size_t
320 dns_name_wire_length_in(dns_label_t *NONNULL name, size_t ret)
321 {
322 // Root label.
323 if (name == NULL)
324 return ret;
325 return dns_name_wire_length_in(name->next, ret + name->len + 1);
326 }
327
328 size_t
329 dns_name_wire_length(dns_label_t *NONNULL name)
330 {
331 return dns_name_wire_length_in(name, 0);
332 }
333
334 // Copy a name we've parsed from a message out in canonical wire format so that we can
335 // use it to verify a signature. As above, not actually needed for copying to a message
336 // we're going to send, since in that case we want to try to compress.
337 static size_t
338 dns_name_to_wire_canonical_in(uint8_t *NONNULL buf, size_t max, size_t ret, dns_label_t *NONNULL name)
339 {
340 if (name == NULL) {
341 return ret;
342 }
343 if (max < name->len + 1) {
344 return 0;
345 }
346 *buf = name->len;
347 memcpy(buf + 1, name->data, name->len);
348 return dns_name_to_wire_canonical_in(buf + name->len + 1,
349 max - name->len - 1, ret + name->len + 1, name->next);
350 }
351
352 size_t
353 dns_name_to_wire_canonical(uint8_t *NONNULL buf, size_t max, dns_label_t *NONNULL name)
354 {
355 return dns_name_to_wire_canonical_in(buf, max, 0, name);
356 }
357
358 // Parse a NUL-terminated text string into a sequence of labels.
359 dns_name_t *
360 dns_pres_name_parse(const char *pname)
361 {
362 const char *dot, *s, *label;
363 dns_label_t *next, *ret, **prev = &ret;
364 size_t len;
365 char *t;
366 char buf[DNS_MAX_LABEL_SIZE];
367 ret = NULL;
368
369 label = pname;
370 dot = strchr(label, '.');
371 while (true) {
372 if (dot == NULL) {
373 dot = label + strlen(label);
374 }
375 len = dot - label;
376 if (len > 0) {
377 t = buf;
378 for (s = label; s < dot; s++) {
379 if (*s == '\\') { // already bounds checked
380 int v0 = s[1] - '0';
381 int v1 = s[2] - '0';
382 int v2 = s[3] - '0';
383 int val = v0 * 100 + v1 * 10 + v2;
384 if (val > 255) {
385 goto fail;
386 }
387 s += 3;
388 *t++ = val;
389 } else {
390 *t++ = *s;
391 }
392 }
393 len = t - buf;
394 }
395 next = calloc(1, len + 1 + (sizeof *next) - DNS_MAX_LABEL_SIZE);
396 if (next == NULL) {
397 goto fail;
398 }
399 *prev = next;
400 prev = &next->next;
401 next->len = len;
402 if (next->len > 0) {
403 memcpy(next->data, buf, next->len);
404 }
405 next->data[next->len] = 0;
406 if (dot[0] == '.' && len > 0) {
407 dot = dot + 1;
408 }
409 if (*dot == '\0') {
410 if (len > 0) {
411 label = dot;
412 } else {
413 break;
414 }
415 } else {
416 label = dot;
417 dot = strchr(label, '.');
418 }
419 }
420 return ret;
421
422 fail:
423 if (ret) {
424 dns_name_free(ret);
425 }
426 return NULL;
427 }
428
429 // See if name is a subdomain of domain. If so, return a pointer to the label in name
430 // where the match to domain begins.
431 dns_name_t *
432 dns_name_subdomain_of(dns_name_t *name, dns_name_t *domain)
433 {
434 int dnum = 0, nnum = 0;
435 dns_name_t *np, *dp;
436
437 for (dp = domain; dp; dp = dp->next) {
438 dnum++;
439 }
440 for (np = name; np; np = np->next) {
441 nnum++;
442 }
443 if (nnum < dnum) {
444 return NULL;
445 }
446 for (np = name; np; np = np->next) {
447 if (nnum-- == dnum) {
448 break;
449 }
450 }
451 if (np != NULL && dns_names_equal(np, domain)) {
452 return np;
453 }
454 return NULL;
455 }
456
457 const char *
458 dns_rcode_name(int rcode)
459 {
460 switch(rcode) {
461 case dns_rcode_noerror:
462 return "No Error";
463 case dns_rcode_formerr:
464 return "Format Error";
465 case dns_rcode_servfail:
466 return "Server Failure";
467 case dns_rcode_nxdomain:
468 return "Non-Existent Domain";
469 case dns_rcode_notimp:
470 return "Not Implemented";
471 case dns_rcode_refused:
472 return "Query Refused";
473 case dns_rcode_yxdomain:
474 return "RFC6672] Name Exists when it should not";
475 case dns_rcode_yxrrset:
476 return "RR Set Exists when it should not";
477 case dns_rcode_nxrrset:
478 return "RR Set that should exist does not";
479 case dns_rcode_notauth:
480 return "Not Authorized";
481 case dns_rcode_notzone:
482 return "Name not contained in zone";
483 case dns_rcode_dsotypeni:
484 return "DSO-Type Not Implemented";
485 case dns_rcode_badvers:
486 return "TSIG Signature Failure";
487 case dns_rcode_badkey:
488 return "Key not recognized";
489 case dns_rcode_badtime:
490 return "Signature out of time window";
491 case dns_rcode_badmode:
492 return "Bad TKEY Mode";
493 case dns_rcode_badname:
494 return "Duplicate key name";
495 case dns_rcode_badalg:
496 return "Algorithm not supported";
497 case dns_rcode_badtrunc:
498 return "Bad Truncation";
499 case dns_rcode_badcookie:
500 return "Bad/missing Server Cookie";
501 default:
502 return "Unknown rcode.";
503 }
504 }
505
506 bool
507 dns_keys_rdata_equal(dns_rr_t *key1, dns_rr_t *key2)
508 {
509 if ((key1->type == dns_rrtype_key && key2->type == dns_rrtype_key) &&
510 key1->data.key.flags == key2->data.key.flags &&
511 key1->data.key.protocol == key2->data.key.protocol &&
512 key1->data.key.algorithm == key2->data.key.algorithm &&
513 key1->data.key.len == key2->data.key.len &&
514 !memcmp(key1->data.key.key, key2->data.key.key, key1->data.key.len))
515 {
516 return true;
517 }
518 return false;
519 }
520
521 // Local Variables:
522 // mode: C
523 // tab-width: 4
524 // c-file-style: "bsd"
525 // c-basic-offset: 4
526 // fill-column: 108
527 // indent-tabs-mode: nil
528 // End: