1 /* wireutils.c 2 * 3 * Copyright (c) 2019-2021 Apple 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 * https://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 #ifndef STANDALONE 48 #define STANDALONE 49 #endif 50 #include "../mDNSCore/DNSCommon.c" 51 52 void dns_name_free(dns_label_t *name) 53 { 54 dns_label_t *next; 55 if (name == NULL) { 56 return; 57 } 58 next = name->next; 59 free(name); 60 if (next != NULL) { 61 return dns_name_free(next); 62 } 63 } 64 65 dns_name_t * 66 dns_name_copy(dns_name_t *original) 67 { 68 dns_name_t *ret = NULL, **cur = &ret; 69 dns_name_t *next; 70 71 for (next = original; next; next = next->next) { 72 *cur = calloc(1, 1 + next->len + (sizeof (dns_name_t)) - DNS_MAX_LABEL_SIZE); 73 if (*cur == NULL) { 74 if (ret != NULL) { 75 dns_name_free(ret); 76 } 77 return NULL; 78 } 79 if (next->len) { 80 memcpy((*cur)->data, next->data, next->len + 1); 81 } 82 (*cur)->len = next->len; 83 cur = &((*cur)->next); 84 } 85 return ret; 86 } 87 88 // Needed for TSIG (RFC2845). 89 void 90 dns_u48_to_wire_(dns_towire_state_t *NONNULL txn, 91 uint64_t val, int line) 92 { 93 if (!txn->error) { 94 if (txn->p + 6 >= txn->lim) { 95 txn->error = ENOBUFS; 96 txn->truncated = true; 97 txn->line = line; 98 return; 99 } 100 *txn->p++ = (val >> 40) & 0xff; 101 *txn->p++ = (val >> 32) & 0xff; 102 *txn->p++ = (val >> 24) & 0xff; 103 *txn->p++ = (val >> 16) & 0xff; 104 *txn->p++ = (val >> 8) & 0xff; 105 *txn->p++ = val & 0xff; 106 } 107 } 108 109 void 110 dns_concatenate_name_to_wire_(dns_towire_state_t *towire, dns_name_t *labels_prefix, const char *prefix, 111 const char *suffix, int line) 112 { 113 dns_wire_t namebuf; 114 dns_towire_state_t namewire; 115 mDNSu8 *ret; 116 namebuf.data[0] = 0; 117 118 // Don't do all this work if we're already past an error. 119 if (towire->error) { 120 return; 121 } 122 memset(&namewire, 0, sizeof namewire); 123 namewire.message = &namebuf; 124 namewire.lim = &namebuf.data[DNS_DATA_SIZE]; 125 namewire.p = namebuf.data; 126 if (prefix != NULL) { 127 dns_name_to_wire(NULL, &namewire, prefix); 128 } else if (labels_prefix != NULL) { 129 size_t bytes_written; 130 131 if (!namewire.error) { 132 bytes_written = (size_t)(namewire.lim - namewire.p); 133 if (bytes_written > INT16_MAX) { 134 towire->error = true; 135 towire->line = __LINE__; 136 return; 137 } 138 bytes_written = dns_name_to_wire_canonical(namewire.p, bytes_written, labels_prefix); 139 // This can never occur with a valid name. 140 if (bytes_written == 0) { 141 namewire.truncated = true; 142 } else { 143 namewire.p += bytes_written; 144 } 145 } 146 } 147 if (suffix != NULL) { 148 dns_full_name_to_wire(NULL, &namewire, suffix); 149 } 150 if (namewire.error) { 151 towire->truncated = namewire.truncated; 152 towire->error = namewire.error; 153 towire->line = line; 154 } 155 156 ret = putDomainNameAsLabels((DNSMessage *)towire->message, towire->p, towire->lim, (domainname *)namebuf.data); 157 if (ret == NULL) { 158 towire->error = ENOBUFS; 159 towire->truncated = true; 160 towire->line = line; 161 return; 162 } 163 164 // Shouldn't happen 165 if (ret > towire->lim) { 166 towire->error = ENOBUFS; 167 towire->truncated = true; 168 towire->line = line; 169 } else { 170 towire->p = ret; 171 } 172 } 173 174 // Convert a dns_name_t to presentation format. Stop conversion at the specified limit. 175 // A trailing dot is only written if a null label is present. 176 const char *NONNULL 177 dns_name_print_to_limit(dns_name_t *NONNULL name, dns_name_t *NULLABLE limit, char *buf, size_t bufmax) 178 { 179 dns_label_t *lp; 180 size_t ix = 0; 181 size_t i; 182 183 // Copy the labels in one at a time, putting a dot between each one; if there isn't room 184 // in the buffer (shouldn't be the case), copy as much as will fit, leaving room for a NUL 185 // termination. 186 for (lp = name; lp != limit && lp != NULL; lp = lp->next) { 187 if (ix != 0) { 188 if (ix + 2 >= bufmax) { 189 break; 190 } 191 buf[ix++] = '.'; 192 } 193 for (i = 0; i < lp->len; i++) { 194 if (isascii(lp->data[i]) && (lp->data[i] == ' ' || isprint(lp->data[i]))) { 195 if (ix + 2 >= bufmax) { 196 break; 197 } 198 buf[ix++] = lp->data[i]; 199 } else { 200 if (ix + 5 >= bufmax) { 201 break; 202 } 203 uint8_t unsigned_char = lp->data[i]; 204 buf[ix++] = '\\'; 205 buf[ix++] = '0' + (unsigned_char / 100); 206 buf[ix++] = '0' + (unsigned_char / 10) % 10; 207 buf[ix++] = '0' + unsigned_char % 10; 208 } 209 } 210 if (i != lp->len) { 211 break; 212 } 213 } 214 buf[ix++] = 0; 215 return buf; 216 } 217 218 const char *NONNULL 219 dns_name_print(dns_name_t *NONNULL name, char *buf, size_t bufmax) 220 { 221 return dns_name_print_to_limit(name, NULL, buf, bufmax); 222 } 223 224 bool 225 dns_labels_equal(const char *label1, const char *label2, size_t len) 226 { 227 unsigned i; 228 for (i = 0; i < len; i++) { 229 if (isascii(label1[i]) && isascii(label2[i])) { 230 if (tolower(label1[i]) != tolower(label2[i])) { 231 return false; 232 } 233 } 234 else { 235 if (label1[i] != label2[i]) { 236 return false; 237 } 238 } 239 } 240 return true; 241 } 242 243 bool 244 dns_names_equal(dns_label_t *NONNULL name1, dns_label_t *NONNULL name2) 245 { 246 if (name1->len != name2->len) { 247 return false; 248 } 249 if (name1->len != 0 && !dns_labels_equal(name1->data, name2->data, name1->len) != 0) { 250 return false; 251 } 252 if (name1->next != NULL && name2->next != NULL) { 253 return dns_names_equal(name1->next, name2->next); 254 } 255 if (name1->next == NULL && name2->next == NULL) { 256 return true; 257 } 258 return false; 259 } 260 261 // Note that "foo.arpa" is not the same as "foo.arpa." 262 bool 263 dns_names_equal_text(dns_label_t *NONNULL name1, const char *NONNULL name2) 264 { 265 const char *ndot; 266 const char *s, *t; 267 int tlen = 0; 268 ndot = strchr(name2, '.'); 269 if (ndot == NULL) { 270 ndot = name2 + strlen(name2); 271 } 272 for (s = name2; s < ndot; s++) { 273 if (*s == '\\') { 274 if (s + 4 <= ndot) { 275 tlen++; 276 s += 3; 277 } else { 278 return false; // An invalid name can't be equal to anything. 279 } 280 } else { 281 tlen++; 282 } 283 } 284 if (name1->len != tlen) { 285 return false; 286 } 287 if (name1->len != 0) { 288 t = name1->data; 289 for (s = name2; s < ndot; s++, t++) { 290 if (*s == '\\') { // already bounds checked 291 int v0 = s[1] - '0'; 292 int v1 = s[2] - '0'; 293 int v2 = s[3] - '0'; 294 int val = v0 * 100 + v1 * 10 + v2; 295 if (val > 255) { 296 return false; 297 } else if (isascii(*s) && isascii(*t)) { 298 if (tolower(*s) != tolower(*t)) { 299 return false; 300 } 301 } else if (val != *t) { 302 return false; 303 } 304 s += 3; 305 } else { 306 if (*s != *t) { 307 return false; 308 } 309 } 310 } 311 } 312 if (name1->next != NULL && *ndot == '.') { 313 return dns_names_equal_text(name1->next, ndot + 1); 314 } 315 if (name1->next == NULL && *ndot == 0) { 316 return true; 317 } 318 return false; 319 } 320 321 // Find the length of a name in uncompressed wire format. 322 static size_t 323 dns_name_wire_length_in(dns_label_t *NONNULL name, size_t ret) 324 { 325 // Root label. 326 if (name == NULL) 327 return ret; 328 return dns_name_wire_length_in(name->next, ret + name->len + 1); 329 } 330 331 size_t 332 dns_name_wire_length(dns_label_t *NONNULL name) 333 { 334 return dns_name_wire_length_in(name, 0); 335 } 336 337 // Copy a name we've parsed from a message out in canonical wire format so that we can 338 // use it to verify a signature. As above, not actually needed for copying to a message 339 // we're going to send, since in that case we want to try to compress. 340 static size_t 341 dns_name_to_wire_canonical_in(uint8_t *NONNULL buf, size_t max, size_t ret, dns_label_t *NONNULL name) 342 { 343 if (name == NULL) { 344 return ret; 345 } 346 if (max < name->len + 1) { 347 return 0; 348 } 349 *buf = name->len; 350 memcpy(buf + 1, name->data, name->len); 351 return dns_name_to_wire_canonical_in(buf + name->len + 1, 352 max - name->len - 1, ret + name->len + 1, name->next); 353 } 354 355 size_t 356 dns_name_to_wire_canonical(uint8_t *NONNULL buf, size_t max, dns_label_t *NONNULL name) 357 { 358 return dns_name_to_wire_canonical_in(buf, max, 0, name); 359 } 360 361 // Parse a NUL-terminated text string into a sequence of labels. 362 dns_name_t * 363 dns_pres_name_parse(const char *pname) 364 { 365 const char *dot, *s, *label; 366 dns_label_t *next, *ret, **prev = &ret; 367 size_t len; 368 char *t; 369 char buf[DNS_MAX_LABEL_SIZE]; 370 ret = NULL; 371 372 label = pname; 373 dot = strchr(label, '.'); 374 while (true) { 375 if (dot == NULL) { 376 dot = label + strlen(label); 377 } 378 len = (size_t)(dot - label); 379 if (len > 0) { 380 t = buf; 381 for (s = label; s < dot; s++) { 382 if (*s == '\\') { // already bounds checked 383 int v0 = s[1] - '0'; 384 int v1 = s[2] - '0'; 385 int v2 = s[3] - '0'; 386 int val = v0 * 100 + v1 * 10 + v2; 387 if (val > 255) { 388 goto fail; 389 } 390 s += 3; 391 *t++ = (char)val; 392 } else { 393 *t++ = *s; 394 } 395 if ((size_t)(t - buf) >= sizeof(buf)) { 396 goto fail; 397 } 398 } 399 len = (size_t)(t - buf); 400 } 401 next = calloc(1, len + 1 + (sizeof *next) - DNS_MAX_LABEL_SIZE); 402 if (next == NULL) { 403 goto fail; 404 } 405 *prev = next; 406 prev = &next->next; 407 next->len = (uint8_t)len; 408 if (next->len > 0) { 409 memcpy(next->data, buf, next->len); 410 } 411 next->data[next->len] = 0; 412 if (dot[0] == '.' && len > 0) { 413 dot = dot + 1; 414 } 415 if (*dot == '\0') { 416 if (len > 0) { 417 label = dot; 418 } else { 419 break; 420 } 421 } else { 422 label = dot; 423 dot = strchr(label, '.'); 424 } 425 } 426 return ret; 427 428 fail: 429 if (ret) { 430 dns_name_free(ret); 431 } 432 return NULL; 433 } 434 435 // See if name is a subdomain of domain. If so, return a pointer to the label in name 436 // where the match to domain begins. 437 dns_name_t * 438 dns_name_subdomain_of(dns_name_t *name, dns_name_t *domain) 439 { 440 int dnum = 0, nnum = 0; 441 dns_name_t *np, *dp; 442 443 for (dp = domain; dp; dp = dp->next) { 444 dnum++; 445 } 446 for (np = name; np; np = np->next) { 447 nnum++; 448 } 449 if (nnum < dnum) { 450 return NULL; 451 } 452 for (np = name; np; np = np->next) { 453 if (nnum-- == dnum) { 454 break; 455 } 456 } 457 if (np != NULL && dns_names_equal(np, domain)) { 458 return np; 459 } 460 return NULL; 461 } 462 463 const char * 464 dns_rcode_name(int rcode) 465 { 466 switch(rcode) { 467 case dns_rcode_noerror: 468 return "No Error"; 469 case dns_rcode_formerr: 470 return "Format Error"; 471 case dns_rcode_servfail: 472 return "Server Failure"; 473 case dns_rcode_nxdomain: 474 return "Non-Existent Domain"; 475 case dns_rcode_notimp: 476 return "Not Implemented"; 477 case dns_rcode_refused: 478 return "Query Refused"; 479 case dns_rcode_yxdomain: 480 return "Name Exists when it should not"; 481 case dns_rcode_yxrrset: 482 return "RR Set Exists when it should not"; 483 case dns_rcode_nxrrset: 484 return "RR Set that should exist does not"; 485 case dns_rcode_notauth: 486 return "Not Authorized"; 487 case dns_rcode_notzone: 488 return "Name not contained in zone"; 489 case dns_rcode_dsotypeni: 490 return "DSO-Type Not Implemented"; 491 case dns_rcode_badvers: 492 return "TSIG Signature Failure"; 493 case dns_rcode_badkey: 494 return "Key not recognized"; 495 case dns_rcode_badtime: 496 return "Signature out of time window"; 497 case dns_rcode_badmode: 498 return "Bad TKEY Mode"; 499 case dns_rcode_badname: 500 return "Duplicate key name"; 501 case dns_rcode_badalg: 502 return "Algorithm not supported"; 503 case dns_rcode_badtrunc: 504 return "Bad Truncation"; 505 case dns_rcode_badcookie: 506 return "Bad/missing Server Cookie"; 507 default: 508 return "Unknown rcode."; 509 } 510 } 511 512 bool 513 dns_keys_rdata_equal(dns_rr_t *key1, dns_rr_t *key2) 514 { 515 if ((key1->type == dns_rrtype_key && key2->type == dns_rrtype_key) && 516 key1->data.key.flags == key2->data.key.flags && 517 key1->data.key.protocol == key2->data.key.protocol && 518 key1->data.key.algorithm == key2->data.key.algorithm && 519 key1->data.key.len == key2->data.key.len && 520 !memcmp(key1->data.key.key, key2->data.key.key, key1->data.key.len)) 521 { 522 return true; 523 } 524 return false; 525 } 526 527 void 528 dns_txt_data_print(char *txt_buf, size_t buf_size, uint16_t txt_length, uint8_t *txt_data) 529 { 530 uint16_t index = 0; 531 char *sp = txt_buf; 532 char *lim = sp + buf_size; 533 const char *continuation_string = ""; 534 const char *commasp = ", "; 535 size_t continuation_length = 0; 536 txt_buf[0] = 0; 537 while (sp < lim && index < txt_length) { 538 uint16_t hunk_len = txt_data[index]; 539 uint16_t next_index = index + hunk_len + 1; 540 541 // Overflow or past the end of data? 542 if (next_index > txt_length || next_index < index) { 543 break; 544 } 545 546 // Out of space, shouldn't be possible. 547 if (sp + continuation_length + 1 >= lim) { 548 break; 549 } 550 if (hunk_len != 0) { 551 if (continuation_length != 0 && sp + continuation_length + 1 < lim) { 552 memcpy(sp, continuation_string, continuation_length); 553 sp += continuation_length; 554 *sp = 0; 555 } 556 continuation_string = commasp; 557 continuation_length = 2; 558 559 for (int i = index + 1; i < index + 1 + hunk_len; i++) { 560 if (isascii(txt_data[i]) && isprint(txt_data[i])) { 561 if (sp + 1 < lim) { 562 *sp++ = txt_data[i]; 563 *sp = 0; 564 } 565 } else { 566 if (sp + 5 < lim) { 567 size_t ret = snprintf(sp, 5, "\%o", txt_data[i]); 568 sp += ret; // Note that this might push sp past lim, which is fine because we'll then exit the loops. 569 } 570 } 571 } 572 } 573 index = next_index; 574 } 575 } 576 577 bool 578 dns_rrs_equal(dns_rr_t *a, dns_rr_t *b, bool rdata_present) 579 { 580 // Obvious stuff first... We do not compare TTL. 581 if (a->type != b->type || a->qclass != b->qclass) { 582 return false; 583 } 584 if (!dns_names_equal(a->name, b->name)) { 585 return false; 586 } 587 if (!rdata_present) { 588 return true; 589 } 590 591 switch(a->type) { 592 // There's no reason to compare invalid RRs, but if we do, they are all equally invalid. 593 case dns_invalid_rr: 594 return true; 595 596 // Anything we don't have a specific format for we store as binary data. 597 default: 598 if (a->data.unparsed.len == b->data.unparsed.len) { 599 return memcmp(a->data.unparsed.data, b->data.unparsed.data, a->data.unparsed.len) == 0; 600 } 601 break; 602 603 // All have a single name as the data 604 case dns_rrtype_ptr: 605 case dns_rrtype_ns: 606 case dns_rrtype_cname: 607 return dns_names_equal(a->data.ptr.name, b->data.ptr.name); 608 609 case dns_rrtype_srv: 610 if (a->data.srv.priority == b->data.srv.priority && 611 a->data.srv.weight == b->data.srv.weight && a->data.srv.port == b->data.srv.port) 612 { 613 return dns_names_equal(a->data.srv.name, b->data.srv.name); 614 } 615 break; 616 617 case dns_rrtype_a: 618 return a->data.a.s_addr == b->data.a.s_addr; 619 620 case dns_rrtype_aaaa: 621 return in6addr_compare(&a->data.aaaa, &b->data.aaaa) == 0; 622 623 // We could compare signatures, but it doesn't really make sense. 624 case dns_rrtype_sig: 625 break; 626 627 case dns_rrtype_key: 628 return dns_keys_rdata_equal(a, b); 629 630 case dns_rrtype_txt: 631 if (a->data.txt.len == b->data.txt.len) { 632 return memcmp(a->data.txt.data, b->data.txt.data, a->data.txt.len) == 0; 633 } 634 } 635 return false; 636 } 637 638 bool 639 dns_rr_to_wire(dns_towire_state_t *towire, dns_rr_t *rr, bool question) 640 { 641 uint8_t *revert = towire->p; 642 643 if (towire->truncated) { 644 return false; 645 } 646 647 // Copy out the invariants. 648 dns_concatenate_name_to_wire(towire, rr->name, NULL, NULL); 649 dns_u16_to_wire(towire, rr->type); 650 dns_u16_to_wire(towire, rr->qclass); 651 652 // Questions don't have RDATA. 653 if (!question) { 654 dns_ttl_to_wire(towire, rr->ttl); 655 dns_rdlength_begin(towire); 656 switch(rr->type) { 657 // There's no reason to compare invalid RRs, but if we do, they are all equally invalid. 658 case dns_invalid_rr: 659 ERROR("invalid rr!"); 660 towire->error = EINVAL; 661 break; 662 663 // Anything we don't have a specific format for we store as binary data. 664 default: 665 dns_rdata_raw_data_to_wire(towire, rr->data.unparsed.data, rr->data.unparsed.len); 666 break; 667 668 case dns_rrtype_soa: 669 dns_concatenate_name_to_wire(towire, rr->data.soa.mname, NULL, NULL); 670 dns_concatenate_name_to_wire(towire, rr->data.soa.rname, NULL, NULL); 671 dns_u32_to_wire(towire, rr->data.soa.serial); 672 dns_u32_to_wire(towire, rr->data.soa.refresh); 673 dns_u32_to_wire(towire, rr->data.soa.retry); 674 dns_u32_to_wire(towire, rr->data.soa.expire); 675 dns_u32_to_wire(towire, rr->data.soa.minimum); 676 break; 677 678 // All have a single name as the data 679 case dns_rrtype_ptr: 680 case dns_rrtype_ns: 681 case dns_rrtype_cname: 682 dns_concatenate_name_to_wire(towire, rr->data.ptr.name, NULL, NULL); 683 break; 684 685 case dns_rrtype_srv: 686 dns_u16_to_wire(towire, rr->data.srv.priority); 687 dns_u16_to_wire(towire, rr->data.srv.weight); 688 dns_u16_to_wire(towire, rr->data.srv.port); 689 dns_concatenate_name_to_wire(towire, rr->data.ptr.name, NULL, NULL); 690 break; 691 692 case dns_rrtype_a: 693 dns_rdata_raw_data_to_wire(towire, &rr->data.a, sizeof(rr->data.a)); 694 break; 695 696 case dns_rrtype_aaaa: 697 dns_rdata_raw_data_to_wire(towire, &rr->data.aaaa, sizeof(rr->data.aaaa)); 698 break; 699 700 // We could compare signatures, but it doesn't really make sense. 701 case dns_rrtype_sig: 702 ERROR("signature not valid here!"); 703 towire->error = EINVAL; 704 break; 705 706 case dns_rrtype_key: 707 ERROR("key not valid here!"); 708 towire->error = EINVAL; 709 break; 710 711 case dns_rrtype_txt: 712 dns_rdata_raw_data_to_wire(towire, rr->data.txt.data, rr->data.txt.len); 713 break; 714 } 715 dns_rdlength_end(towire); 716 } 717 718 if (towire->truncated || towire->error) { 719 towire->p = revert; 720 return false; 721 } 722 return true; 723 } 724 725 void 726 dns_message_rrs_to_wire(dns_towire_state_t *towire, dns_message_t *message) 727 { 728 bool question = true; 729 for (int i = 0; i < 4; i++) { 730 int count = 0; 731 dns_rr_t *rrs = NULL; 732 switch(i) { 733 case 0: count = message->qdcount; rrs = message->questions; break; 734 case 1: count = message->ancount; rrs = message->answers; break; 735 case 2: count = message->nscount; rrs = message->authority; break; 736 case 3: count = message->arcount; rrs = message->additional; break; 737 } 738 739 for (int j = 0; j < count; j++) { 740 dns_rr_t *rr = &rrs[j]; 741 if (!dns_rr_to_wire(towire, rr, question)) { 742 // XXX if it's TCP we really need to embiggen here. 743 ERROR("no space in message for rr %d/%d %d", i, j, rr->type); 744 } 745 } 746 question = false; 747 } 748 } 749 750 // Local Variables: 751 // mode: C 752 // tab-width: 4 753 // c-file-style: "bsd" 754 // c-basic-offset: 4 755 // fill-column: 108 756 // indent-tabs-mode: nil 757 // End: 758