1 /* $NetBSD: ns_name.c,v 1.17 2026/09/01 19:22:21 andvar Exp $ */ 2 3 /* 4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (c) 1996,1999 by Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/cdefs.h> 21 #ifndef lint 22 #ifdef notdef 23 static const char rcsid[] = "Id: ns_name.c,v 1.11 2009/01/23 19:59:16 each Exp"; 24 #else 25 __RCSID("$NetBSD: ns_name.c,v 1.17 2026/09/01 19:22:21 andvar Exp $"); 26 #endif 27 #endif 28 29 #include "port_before.h" 30 31 #include <sys/types.h> 32 33 #include <netinet/in.h> 34 #include <arpa/nameser.h> 35 36 #include <assert.h> 37 #include <errno.h> 38 #include <resolv.h> 39 #include <string.h> 40 #include <ctype.h> 41 #include <stdlib.h> 42 #include <limits.h> 43 44 #include "port_after.h" 45 46 #define NS_TYPE_ELT 0x40 /*%< EDNS0 extended label type */ 47 #define DNS_LABELTYPE_BITSTRING 0x41 48 49 /* Data. */ 50 51 static const char digits[] = "0123456789"; 52 53 static const char digitvalue[256] = { 54 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/ 55 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/ 56 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/ 57 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/ 58 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/ 59 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/ 60 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/ 61 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/ 62 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 63 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 64 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 65 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 66 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 67 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 68 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 69 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/ 70 }; 71 72 /* Forward. */ 73 74 static int special(int); 75 static int printable(int); 76 static int dn_find(const u_char *, const u_char *, 77 const u_char * const *, 78 const u_char * const *); 79 static int encode_bitsring(const char **, const char *, 80 unsigned char **, unsigned char **, 81 unsigned const char *); 82 static int labellen(const u_char *); 83 static int decode_bitstring(const unsigned char **, 84 char *, const char *); 85 86 /* Public. */ 87 88 /*% 89 * Convert an encoded domain name to printable ascii as per RFC1035. 90 91 * return: 92 *\li Number of bytes written to buffer, or -1 (with errno set) 93 * 94 * notes: 95 *\li The root is returned as "." 96 *\li All other domains are returned in non absolute form 97 */ 98 int 99 ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) 100 { 101 const u_char *cp; 102 char *dn, *eom; 103 u_char c; 104 u_int n; 105 int l; 106 107 cp = src; 108 dn = dst; 109 eom = dst + dstsiz; 110 111 while ((n = *cp++) != 0) { 112 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { 113 /* Some kind of compression pointer. */ 114 errno = EMSGSIZE; 115 return (-1); 116 } 117 if (dn != dst) { 118 if (dn >= eom) { 119 errno = EMSGSIZE; 120 return (-1); 121 } 122 *dn++ = '.'; 123 } 124 if ((l = labellen(cp - 1)) < 0) { 125 errno = EMSGSIZE; /*%< XXX */ 126 return (-1); 127 } 128 if (dn + l >= eom) { 129 errno = EMSGSIZE; 130 return (-1); 131 } 132 if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) { 133 int m; 134 135 if (n != DNS_LABELTYPE_BITSTRING) { 136 /* XXX: labellen should reject this case */ 137 errno = EINVAL; 138 return (-1); 139 } 140 if ((m = decode_bitstring(&cp, dn, eom)) < 0) 141 { 142 errno = EMSGSIZE; 143 return (-1); 144 } 145 dn += m; 146 continue; 147 } 148 for (; l > 0; l--) { 149 c = *cp++; 150 if (special(c)) { 151 if (dn + 1 >= eom) { 152 errno = EMSGSIZE; 153 return (-1); 154 } 155 *dn++ = '\\'; 156 *dn++ = (char)c; 157 } else if (!printable(c)) { 158 if (dn + 3 >= eom) { 159 errno = EMSGSIZE; 160 return (-1); 161 } 162 *dn++ = '\\'; 163 *dn++ = digits[c / 100]; 164 *dn++ = digits[(c % 100) / 10]; 165 *dn++ = digits[c % 10]; 166 } else { 167 if (dn >= eom) { 168 errno = EMSGSIZE; 169 return (-1); 170 } 171 *dn++ = (char)c; 172 } 173 } 174 } 175 if (dn == dst) { 176 if (dn >= eom) { 177 errno = EMSGSIZE; 178 return (-1); 179 } 180 *dn++ = '.'; 181 } 182 if (dn >= eom) { 183 errno = EMSGSIZE; 184 return (-1); 185 } 186 *dn++ = '\0'; 187 _DIAGASSERT(__type_fit(int, dn - dst)); 188 return (int)(dn - dst); 189 } 190 191 /*% 192 * Convert a ascii string into an encoded domain name as per RFC1035. 193 * 194 * return: 195 * 196 *\li -1 if it fails 197 *\li 1 if string was fully qualified 198 *\li 0 is string was not fully qualified 199 * 200 * notes: 201 *\li Enforces label and domain length limits. 202 */ 203 int 204 ns_name_pton(const char *src, u_char *dst, size_t dstsiz) { 205 return (ns_name_pton2(src, dst, dstsiz, NULL)); 206 } 207 208 /* 209 * ns_name_pton2(src, dst, dstsiz, *dstlen) 210 * Convert a ascii string into an encoded domain name as per RFC1035. 211 * return: 212 * -1 if it fails 213 * 1 if string was fully qualified 214 * 0 is string was not fully qualified 215 * side effects: 216 * fills in *dstlen (if non-NULL) 217 * notes: 218 * Enforces label and domain length limits. 219 */ 220 int 221 ns_name_pton2(const char *src, u_char *dst, size_t dstsiz, size_t *dstlen) { 222 u_char *label, *bp, *eom; 223 int c, n, escaped, e = 0; 224 const char *cp; 225 226 escaped = 0; 227 bp = dst; 228 eom = dst + dstsiz; 229 label = bp++; 230 231 while ((c = *src++) != 0) { 232 if (escaped) { 233 if (c == '[') { /*%< start a bit string label */ 234 if ((cp = strchr(src, ']')) == NULL) { 235 errno = EINVAL; /*%< ??? */ 236 return (-1); 237 } 238 if ((e = encode_bitsring(&src, cp + 2, 239 &label, &bp, eom)) 240 != 0) { 241 errno = e; 242 return (-1); 243 } 244 escaped = 0; 245 label = bp++; 246 if ((c = *src++) == 0) 247 goto done; 248 else if (c != '.') { 249 errno = EINVAL; 250 return (-1); 251 } 252 continue; 253 } 254 else if ((cp = strchr(digits, c)) != NULL) { 255 n = (int)(cp - digits) * 100; 256 if ((c = *src++) == 0 || 257 (cp = strchr(digits, c)) == NULL) { 258 errno = EMSGSIZE; 259 return (-1); 260 } 261 n += (int)(cp - digits) * 10; 262 if ((c = *src++) == 0 || 263 (cp = strchr(digits, c)) == NULL) { 264 errno = EMSGSIZE; 265 return (-1); 266 } 267 n += (int)(cp - digits); 268 if (n > 255) { 269 errno = EMSGSIZE; 270 return (-1); 271 } 272 c = n; 273 } 274 escaped = 0; 275 } else if (c == '\\') { 276 escaped = 1; 277 continue; 278 } else if (c == '.') { 279 c = (int)(bp - label - 1); 280 if ((c & NS_CMPRSFLGS) != 0) { /*%< Label too big. */ 281 errno = EMSGSIZE; 282 return (-1); 283 } 284 if (label >= eom) { 285 errno = EMSGSIZE; 286 return (-1); 287 } 288 *label = c; 289 /* Fully qualified ? */ 290 if (*src == '\0') { 291 if (c != 0) { 292 if (bp >= eom) { 293 errno = EMSGSIZE; 294 return (-1); 295 } 296 *bp++ = '\0'; 297 } 298 if ((bp - dst) > MAXCDNAME) { 299 errno = EMSGSIZE; 300 return (-1); 301 } 302 if (dstlen != NULL) 303 *dstlen = (bp - dst); 304 return (1); 305 } 306 if (c == 0 || *src == '.') { 307 errno = EMSGSIZE; 308 return (-1); 309 } 310 label = bp++; 311 continue; 312 } 313 if (bp >= eom) { 314 errno = EMSGSIZE; 315 return (-1); 316 } 317 *bp++ = (u_char)c; 318 } 319 c = (int)(bp - label - 1); 320 if ((c & NS_CMPRSFLGS) != 0) { /*%< Label too big. */ 321 errno = EMSGSIZE; 322 return (-1); 323 } 324 done: 325 if (label >= eom) { 326 errno = EMSGSIZE; 327 return (-1); 328 } 329 *label = c; 330 if (c != 0) { 331 if (bp >= eom) { 332 errno = EMSGSIZE; 333 return (-1); 334 } 335 *bp++ = 0; 336 } 337 if ((bp - dst) > MAXCDNAME) { /*%< src too big */ 338 errno = EMSGSIZE; 339 return (-1); 340 } 341 if (dstlen != NULL) 342 *dstlen = (bp - dst); 343 return (0); 344 } 345 346 /*% 347 * Convert a network strings labels into all lowercase. 348 * 349 * return: 350 *\li Number of bytes written to buffer, or -1 (with errno set) 351 * 352 * notes: 353 *\li Enforces label and domain length limits. 354 */ 355 356 int 357 ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) 358 { 359 const u_char *cp; 360 u_char *dn, *eom; 361 u_char c; 362 u_int n; 363 int l; 364 365 cp = src; 366 dn = dst; 367 eom = dst + dstsiz; 368 369 if (dn >= eom) { 370 errno = EMSGSIZE; 371 return (-1); 372 } 373 while ((n = *cp++) != 0) { 374 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { 375 /* Some kind of compression pointer. */ 376 errno = EMSGSIZE; 377 return (-1); 378 } 379 *dn++ = n; 380 if ((l = labellen(cp - 1)) < 0) { 381 errno = EMSGSIZE; 382 return (-1); 383 } 384 if (dn + l >= eom) { 385 errno = EMSGSIZE; 386 return (-1); 387 } 388 for (; l > 0; l--) { 389 c = *cp++; 390 if (isascii(c) && isupper(c)) 391 *dn++ = tolower(c); 392 else 393 *dn++ = c; 394 } 395 } 396 *dn++ = '\0'; 397 _DIAGASSERT(__type_fit(int, dn - dst)); 398 return (int)(dn - dst); 399 } 400 401 /*% 402 * Unpack a domain name from a message, source may be compressed. 403 * 404 * return: 405 *\li -1 if it fails, or consumed octets if it succeeds. 406 */ 407 int 408 ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, 409 u_char *dst, size_t dstsiz) 410 { 411 return (ns_name_unpack2(msg, eom, src, dst, dstsiz, NULL)); 412 } 413 414 /* 415 * ns_name_unpack2(msg, eom, src, dst, dstsiz, *dstlen) 416 * Unpack a domain name from a message, source may be compressed. 417 * return: 418 * -1 if it fails, or consumed octets if it succeeds. 419 * side effect: 420 * fills in *dstlen (if non-NULL). 421 */ 422 int 423 ns_name_unpack2(const u_char *msg, const u_char *eom, const u_char *src, 424 u_char *dst, size_t dstsiz, size_t *dstlen) 425 { 426 const u_char *srcp, *dstlim; 427 u_char *dstp; 428 int n, len, checked, l; 429 430 len = -1; 431 checked = 0; 432 dstp = dst; 433 srcp = src; 434 dstlim = dst + dstsiz; 435 if (srcp < msg || srcp >= eom) { 436 errno = EMSGSIZE; 437 return (-1); 438 } 439 /* Fetch next label in domain name. */ 440 while ((n = *srcp++) != 0) { 441 /* Check for indirection. */ 442 switch (n & NS_CMPRSFLGS) { 443 case 0: 444 case NS_TYPE_ELT: 445 /* Limit checks. */ 446 if ((l = labellen(srcp - 1)) < 0) { 447 errno = EMSGSIZE; 448 return (-1); 449 } 450 if (dstp + l + 1 >= dstlim || srcp + l >= eom) { 451 errno = EMSGSIZE; 452 return (-1); 453 } 454 checked += l + 1; 455 *dstp++ = n; 456 memcpy(dstp, srcp, (size_t)l); 457 dstp += l; 458 srcp += l; 459 break; 460 461 case NS_CMPRSFLGS: 462 if (srcp >= eom) { 463 errno = EMSGSIZE; 464 return (-1); 465 } 466 if (len < 0) { 467 _DIAGASSERT(__type_fit(int, srcp - src + 1)); 468 len = (int)(srcp - src + 1); 469 } 470 n = ((n & 0x3f) << 8) | (*srcp & 0xff); 471 if (n >= eom - msg) { /*%< Out of range. */ 472 errno = EMSGSIZE; 473 return (-1); 474 } 475 srcp = msg + n; 476 checked += 2; 477 /* 478 * Check for loops in the compressed name; 479 * if we've looked at the whole message, 480 * there must be a loop. 481 */ 482 if (checked >= eom - msg) { 483 errno = EMSGSIZE; 484 return (-1); 485 } 486 break; 487 488 default: 489 errno = EMSGSIZE; 490 return (-1); /*%< flag error */ 491 } 492 } 493 *dstp++ = 0; 494 if (dstlen != NULL) 495 *dstlen = dstp - dst; 496 if (len < 0) { 497 _DIAGASSERT(__type_fit(int, srcp - src)); 498 len = (int)(srcp - src); 499 } 500 return len; 501 } 502 503 /*% 504 * Pack domain name 'domain' into 'comp_dn'. 505 * 506 * return: 507 *\li Size of the compressed name, or -1. 508 * 509 * notes: 510 *\li 'dnptrs' is an array of pointers to previous compressed names. 511 *\li dnptrs[0] is a pointer to the beginning of the message. The array 512 * ends with NULL. 513 *\li 'lastdnptr' is a pointer to the end of the array pointed to 514 * by 'dnptrs'. 515 * 516 * Side effects: 517 *\li The list of pointers in dnptrs is updated for labels inserted into 518 * the message as we compress the name. If 'dnptr' is NULL, we don't 519 * try to compress names. If 'lastdnptr' is NULL, we don't update the 520 * list. 521 */ 522 int 523 ns_name_pack(const u_char *src, u_char *dst, int dstsiz, 524 const u_char **dnptrs, const u_char **lastdnptr) 525 { 526 u_char *dstp; 527 const u_char **cpp, **lpp, *eob, *msg; 528 const u_char *srcp; 529 int n, l, first = 1; 530 531 srcp = src; 532 dstp = dst; 533 eob = dstp + dstsiz; 534 lpp = cpp = NULL; 535 if (dnptrs != NULL) { 536 if ((msg = *dnptrs++) != NULL) { 537 for (cpp = dnptrs; *cpp != NULL; cpp++) 538 continue; 539 lpp = cpp; /*%< end of list to search */ 540 } 541 } else 542 msg = NULL; 543 544 /* make sure the domain we are about to add is legal */ 545 l = 0; 546 do { 547 int l0; 548 549 n = *srcp; 550 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { 551 errno = EMSGSIZE; 552 return (-1); 553 } 554 if ((l0 = labellen(srcp)) < 0) { 555 errno = EINVAL; 556 return (-1); 557 } 558 l += l0 + 1; 559 if (l > MAXCDNAME) { 560 errno = EMSGSIZE; 561 return (-1); 562 } 563 srcp += l0 + 1; 564 } while (n != 0); 565 566 /* from here on we need to reset compression pointer array on error */ 567 srcp = src; 568 do { 569 /* Look to see if we can use pointers. */ 570 n = *srcp; 571 if (n != 0 && msg != NULL) { 572 l = dn_find(srcp, msg, (const u_char * const *)dnptrs, 573 (const u_char * const *)lpp); 574 if (l >= 0) { 575 if (dstp + 1 >= eob) { 576 goto cleanup; 577 } 578 *dstp++ = ((u_int32_t)l >> 8) | NS_CMPRSFLGS; 579 *dstp++ = l % 256; 580 _DIAGASSERT(__type_fit(int, dstp - dst)); 581 return (int)(dstp - dst); 582 } 583 /* Not found, save it. */ 584 if (lastdnptr != NULL && cpp < lastdnptr - 1 && 585 (dstp - msg) < 0x4000 && first) { 586 *cpp++ = dstp; 587 *cpp = NULL; 588 first = 0; 589 } 590 } 591 /* copy label to buffer */ 592 if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { 593 /* Should not happen. */ 594 goto cleanup; 595 } 596 n = labellen(srcp); 597 if (dstp + 1 + n >= eob) { 598 goto cleanup; 599 } 600 memcpy(dstp, srcp, (size_t)(n + 1)); 601 srcp += n + 1; 602 dstp += n + 1; 603 } while (n != 0); 604 605 if (dstp > eob) { 606 cleanup: 607 if (msg != NULL) 608 *lpp = NULL; 609 errno = EMSGSIZE; 610 return (-1); 611 } 612 _DIAGASSERT(__type_fit(int, dstp - dst)); 613 return (int)(dstp - dst); 614 } 615 616 /*% 617 * Expand compressed domain name to presentation format. 618 * 619 * return: 620 *\li Number of bytes read out of `src', or -1 (with errno set). 621 * 622 * note: 623 *\li Root domain returns as "." not "". 624 */ 625 int 626 ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src, 627 char *dst, size_t dstsiz) 628 { 629 u_char tmp[NS_MAXCDNAME]; 630 int n; 631 632 if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1) 633 return (-1); 634 if (ns_name_ntop(tmp, dst, dstsiz) == -1) 635 return (-1); 636 return (n); 637 } 638 639 /*% 640 * Compress a domain name into wire format, using compression pointers. 641 * 642 * return: 643 *\li Number of bytes consumed in `dst' or -1 (with errno set). 644 * 645 * notes: 646 *\li 'dnptrs' is an array of pointers to previous compressed names. 647 *\li dnptrs[0] is a pointer to the beginning of the message. 648 *\li The list ends with NULL. 'lastdnptr' is a pointer to the end of the 649 * array pointed to by 'dnptrs'. Side effect is to update the list of 650 * pointers for labels inserted into the message as we compress the name. 651 *\li If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' 652 * is NULL, we don't update the list. 653 */ 654 int 655 ns_name_compress(const char *src, u_char *dst, size_t dstsiz, 656 const u_char **dnptrs, const u_char **lastdnptr) 657 { 658 u_char tmp[NS_MAXCDNAME]; 659 660 if (ns_name_pton(src, tmp, sizeof tmp) == -1) 661 return (-1); 662 return (ns_name_pack(tmp, dst, (int)dstsiz, dnptrs, lastdnptr)); 663 } 664 665 /*% 666 * Reset dnptrs so that there are no active references to pointers at or 667 * after src. 668 */ 669 void 670 ns_name_rollback(const u_char *src, const u_char **dnptrs, 671 const u_char **lastdnptr) 672 { 673 while (dnptrs < lastdnptr && *dnptrs != NULL) { 674 if (*dnptrs >= src) { 675 *dnptrs = NULL; 676 break; 677 } 678 dnptrs++; 679 } 680 } 681 682 /*% 683 * Advance *ptrptr to skip over the compressed name it points at. 684 * 685 * return: 686 *\li 0 on success, -1 (with errno set) on failure. 687 */ 688 int 689 ns_name_skip(const u_char **ptrptr, const u_char *eom) 690 { 691 const u_char *cp; 692 u_int n; 693 int l = 0; 694 695 cp = *ptrptr; 696 while (cp < eom && (n = *cp++) != 0) { 697 /* Check for indirection. */ 698 switch (n & NS_CMPRSFLGS) { 699 case 0: /*%< normal case, n == len */ 700 cp += n; 701 continue; 702 case NS_TYPE_ELT: /*%< EDNS0 extended label */ 703 if (cp < eom && (l = labellen(cp - 1)) < 0) { 704 errno = EMSGSIZE; /*%< XXX */ 705 return (-1); 706 } 707 cp += l; 708 continue; 709 case NS_CMPRSFLGS: /*%< indirection */ 710 cp++; 711 break; 712 default: /*%< illegal type */ 713 errno = EMSGSIZE; 714 return (-1); 715 } 716 break; 717 } 718 if (cp > eom) { 719 errno = EMSGSIZE; 720 return (-1); 721 } 722 *ptrptr = cp; 723 return (0); 724 } 725 726 /* Find the number of octets an nname takes up, including the root label. 727 * (This is basically ns_name_skip() without compression-pointer support.) 728 * ((NOTE: can only return zero if passed-in namesiz argument is zero.)) 729 */ 730 ssize_t 731 ns_name_length(ns_nname_ct nname, size_t namesiz) { 732 ns_nname_ct orig = nname; 733 u_int n; 734 735 while (namesiz-- > 0 && (n = *nname++) != 0) { 736 if ((n & NS_CMPRSFLGS) != 0) { 737 errno = EISDIR; 738 return (-1); 739 } 740 if (n > namesiz) { 741 errno = EMSGSIZE; 742 return (-1); 743 } 744 nname += n; 745 namesiz -= n; 746 } 747 return (nname - orig); 748 } 749 750 /* Compare two nname's for equality. Return -1 on error (setting errno). 751 */ 752 int 753 ns_name_eq(ns_nname_ct a, size_t as, ns_nname_ct b, size_t bs) { 754 ns_nname_ct ae = a + as, be = b + bs; 755 int ac, bc; 756 757 while (ac = *a, bc = *b, ac != 0 && bc != 0) { 758 if ((ac & NS_CMPRSFLGS) != 0 || (bc & NS_CMPRSFLGS) != 0) { 759 errno = EISDIR; 760 return (-1); 761 } 762 if (a + ac >= ae || b + bc >= be) { 763 errno = EMSGSIZE; 764 return (-1); 765 } 766 if (ac != bc || strncasecmp((const char *) ++a, 767 (const char *) ++b, 768 (size_t)ac) != 0) 769 return (0); 770 a += ac, b += bc; 771 } 772 return (ac == 0 && bc == 0); 773 } 774 775 /* Is domain "A" owned by (at or below) domain "B"? 776 */ 777 int 778 ns_name_owned(ns_namemap_ct a, int an, ns_namemap_ct b, int bn) { 779 /* If A is shorter, it cannot be owned by B. */ 780 if (an < bn) 781 return (0); 782 783 /* If they are unequal before the length of the shorter, A cannot... */ 784 while (bn > 0) { 785 if (a->len != b->len || 786 strncasecmp((const char *) a->base, 787 (const char *) b->base, (size_t)a->len) != 0) 788 return (0); 789 a++, an--; 790 b++, bn--; 791 } 792 793 /* A might be longer or not, but either way, B owns it. */ 794 return (1); 795 } 796 797 /* Build an array of <base,len> tuples from an nname, top-down order. 798 * Return the number of tuples (labels) thus discovered. 799 */ 800 int 801 ns_name_map(ns_nname_ct nname, size_t namelen, ns_namemap_t map, int mapsize) { 802 u_int n; 803 int l; 804 805 n = *nname++; 806 namelen--; 807 808 /* Root zone? */ 809 if (n == 0) { 810 /* Extra data follows name? */ 811 if (namelen > 0) { 812 errno = EMSGSIZE; 813 return (-1); 814 } 815 return (0); 816 } 817 818 /* Compression pointer? */ 819 if ((n & NS_CMPRSFLGS) != 0) { 820 errno = EISDIR; 821 return (-1); 822 } 823 824 /* Label too long? */ 825 if (n > namelen) { 826 errno = EMSGSIZE; 827 return (-1); 828 } 829 830 /* Recurse to get rest of name done first. */ 831 l = ns_name_map(nname + n, namelen - n, map, mapsize); 832 if (l < 0) 833 return (-1); 834 835 /* Too many labels? */ 836 if (l >= mapsize) { 837 errno = ENAMETOOLONG; 838 return (-1); 839 } 840 841 /* We're on our way back up-stack, store current map data. */ 842 map[l].base = nname; 843 map[l].len = n; 844 return (l + 1); 845 } 846 847 /* Count the labels in a domain name. Root counts, so COM. has two. This 848 * is to make the result comparable to the result of ns_name_map(). 849 */ 850 int 851 ns_name_labels(ns_nname_ct nname, size_t namesiz) { 852 int ret = 0; 853 u_int n; 854 855 while (namesiz-- > 0 && (n = *nname++) != 0) { 856 if ((n & NS_CMPRSFLGS) != 0) { 857 errno = EISDIR; 858 return (-1); 859 } 860 if (n > namesiz) { 861 errno = EMSGSIZE; 862 return (-1); 863 } 864 nname += n; 865 namesiz -= n; 866 ret++; 867 } 868 return (ret + 1); 869 } 870 871 /* Private. */ 872 873 /*% 874 * Thinking in noninternationalized USASCII (per the DNS spec), 875 * is this character special ("in need of quoting") ? 876 * 877 * return: 878 *\li boolean. 879 */ 880 static int 881 special(int ch) { 882 switch (ch) { 883 case 0x22: /*%< '"' */ 884 case 0x2E: /*%< '.' */ 885 case 0x3B: /*%< ';' */ 886 case 0x5C: /*%< '\\' */ 887 case 0x28: /*%< '(' */ 888 case 0x29: /*%< ')' */ 889 /* Special modifiers in zone files. */ 890 case 0x40: /*%< '@' */ 891 case 0x24: /*%< '$' */ 892 return (1); 893 default: 894 return (0); 895 } 896 } 897 898 /*% 899 * Thinking in noninternationalized USASCII (per the DNS spec), 900 * is this character visible and not a space when printed ? 901 * 902 * return: 903 *\li boolean. 904 */ 905 static int 906 printable(int ch) { 907 return (ch > 0x20 && ch < 0x7f); 908 } 909 910 /*% 911 * Thinking in noninternationalized USASCII (per the DNS spec), 912 * convert this character to lower case if it's upper case. 913 */ 914 static int 915 mklower(int ch) { 916 if (ch >= 0x41 && ch <= 0x5A) 917 return (ch + 0x20); 918 return (ch); 919 } 920 921 /*% 922 * Search for the counted-label name in an array of compressed names. 923 * 924 * return: 925 *\li offset from msg if found, or -1. 926 * 927 * notes: 928 *\li dnptrs is the pointer to the first name on the list, 929 *\li not the pointer to the start of the message. 930 */ 931 static int 932 dn_find(const u_char *domain, const u_char *msg, 933 const u_char * const *dnptrs, 934 const u_char * const *lastdnptr) 935 { 936 const u_char *dn, *cp, *sp; 937 const u_char * const *cpp; 938 u_int n; 939 940 for (cpp = dnptrs; cpp < lastdnptr; cpp++) { 941 sp = *cpp; 942 /* 943 * terminate search on: 944 * root label 945 * compression pointer 946 * unusable offset 947 */ 948 while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 && 949 (sp - msg) < 0x4000) { 950 dn = domain; 951 cp = sp; 952 while ((n = *cp++) != 0) { 953 /* 954 * check for indirection 955 */ 956 switch (n & NS_CMPRSFLGS) { 957 case 0: /*%< normal case, n == len */ 958 n = labellen(cp - 1); /*%< XXX */ 959 if (n != *dn++) 960 goto next; 961 962 for (; n > 0; n--) 963 if (mklower(*dn++) != 964 mklower(*cp++)) 965 goto next; 966 /* Is next root for both ? */ 967 if (*dn == '\0' && *cp == '\0') { 968 _DIAGASSERT(__type_fit(int, 969 sp - msg)); 970 return (int)(sp - msg); 971 } 972 if (*dn) 973 continue; 974 goto next; 975 case NS_CMPRSFLGS: /*%< indirection */ 976 cp = msg + (((n & 0x3f) << 8) | *cp); 977 break; 978 979 default: /*%< illegal type */ 980 errno = EMSGSIZE; 981 return (-1); 982 } 983 } 984 next: ; 985 sp += *sp + 1; 986 } 987 } 988 errno = ENOENT; 989 return (-1); 990 } 991 992 static int 993 decode_bitstring(const unsigned char **cpp, char *dn, const char *eom) 994 { 995 const unsigned char *cp = *cpp; 996 char tc; 997 int b, blen, t, l; 998 size_t size = (size_t)(eom - dn); 999 1000 if ((blen = (*cp & 0xff)) == 0) 1001 blen = 256; 1002 cp++; 1003 t = 0; 1004 ADDS(snprintf(dn + t, size - t, "\\[x")); 1005 for (b = blen; b > 7; b -= 8, cp++) { 1006 ADDS(snprintf(dn + t, size - t, "%02x", *cp & 0xff)); 1007 } 1008 if (b > 4) { 1009 tc = *cp++; 1010 ADDS(snprintf(dn + t, size - t, "%02x", 1011 tc & (0xff << (8 - b)))); 1012 } else if (b > 0) { 1013 tc = *cp++; 1014 ADDS(snprintf(dn + t, size - t, "%1x", 1015 (((u_int32_t)tc >> 4) & 0x0f) & (0x0f << (4 - b)))); 1016 } 1017 ADDS(snprintf(dn + t, size - t, "/%d]", blen)); 1018 1019 *cpp = cp; 1020 return t; 1021 emsgsize: 1022 return -1; 1023 } 1024 1025 static int 1026 encode_bitsring(const char **bp, const char *end, unsigned char **labelp, 1027 unsigned char ** dst, unsigned const char *eom) 1028 { 1029 int afterslash = 0; 1030 const char *cp = *bp; 1031 unsigned char *tp; 1032 char c; 1033 const char *beg_blen; 1034 char *end_blen = NULL; 1035 int value = 0, count = 0, tbcount = 0, blen = 0; 1036 1037 beg_blen = end_blen = NULL; 1038 1039 /* a bitstring must contain at least 2 characters */ 1040 if (end - cp < 2) 1041 return (EINVAL); 1042 1043 /* XXX: currently, only hex strings are supported */ 1044 if (*cp++ != 'x') 1045 return (EINVAL); 1046 if (!isxdigit((*cp) & 0xff)) /*%< reject '\[x/BLEN]' */ 1047 return (EINVAL); 1048 1049 for (tp = *dst + 1; cp < end && tp < eom; cp++) { 1050 switch((c = *cp)) { 1051 case ']': /*%< end of the bitstring */ 1052 if (afterslash) { 1053 if (beg_blen == NULL) 1054 return (EINVAL); 1055 blen = (int)strtol(beg_blen, &end_blen, 10); 1056 if (*end_blen != ']') 1057 return (EINVAL); 1058 } 1059 if (count) 1060 *tp++ = ((value << 4) & 0xff); 1061 cp++; /*%< skip ']' */ 1062 goto done; 1063 case '/': 1064 afterslash = 1; 1065 break; 1066 default: 1067 if (afterslash) { 1068 if (!isdigit(c&0xff)) 1069 return (EINVAL); 1070 if (beg_blen == NULL) { 1071 1072 if (c == '0') { 1073 /* blen never begins with 0 */ 1074 return (EINVAL); 1075 } 1076 beg_blen = cp; 1077 } 1078 } else { 1079 if (!isxdigit(c&0xff)) 1080 return (EINVAL); 1081 value <<= 4; 1082 value += digitvalue[(int)c]; 1083 count += 4; 1084 tbcount += 4; 1085 if (tbcount > 256) 1086 return (EINVAL); 1087 if (count == 8) { 1088 *tp++ = value; 1089 count = 0; 1090 } 1091 } 1092 break; 1093 } 1094 } 1095 done: 1096 if (cp >= end || tp >= eom) 1097 return (EMSGSIZE); 1098 1099 /* 1100 * bit length validation: 1101 * If a <length> is present, the number of digits in the <bit-data> 1102 * MUST be just sufficient to contain the number of bits specified 1103 * by the <length>. If there are insignificant bits in a final 1104 * hexadecimal or octal digit, they MUST be zero. 1105 * RFC2673, Section 3.2. 1106 */ 1107 if (blen > 0) { 1108 int traillen; 1109 1110 if (((blen + 3) & ~3) != tbcount) 1111 return (EINVAL); 1112 traillen = tbcount - blen; /*%< between 0 and 3 */ 1113 if (((value << (8 - traillen)) & 0xff) != 0) 1114 return (EINVAL); 1115 } 1116 else 1117 blen = tbcount; 1118 if (blen == 256) 1119 blen = 0; 1120 1121 /* encode the type and the significant bit fields */ 1122 **labelp = DNS_LABELTYPE_BITSTRING; 1123 **dst = blen; 1124 1125 *bp = cp; 1126 *dst = tp; 1127 1128 return (0); 1129 } 1130 1131 static int 1132 labellen(const u_char *lp) 1133 { 1134 int bitlen; 1135 u_char l = *lp; 1136 1137 if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) { 1138 /* should be avoided by the caller */ 1139 return (-1); 1140 } 1141 1142 if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) { 1143 if (l == DNS_LABELTYPE_BITSTRING) { 1144 if ((bitlen = *(lp + 1)) == 0) 1145 bitlen = 256; 1146 return ((bitlen + 7 ) / 8 + 1); 1147 } 1148 return (-1); /*%< unknown ELT */ 1149 } 1150 return (l); 1151 } 1152 1153 /*! \file */ 1154