1/* 2 * Copyright (c) 1996 by Internet Software Consortium. 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 9 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 10 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 11 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 13 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 14 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 15 * SOFTWARE. 16 */ 17 18/* 19 * Portions copyright (c) 1999, 2000 20 * Intel Corporation. 21 * All rights reserved. 22 * 23 * Redistribution and use in source and binary forms, with or without 24 * modification, are permitted provided that the following conditions 25 * are met: 26 * 27 * 1. Redistributions of source code must retain the above copyright 28 * notice, this list of conditions and the following disclaimer. 29 * 30 * 2. Redistributions in binary form must reproduce the above copyright 31 * notice, this list of conditions and the following disclaimer in the 32 * documentation and/or other materials provided with the distribution. 33 * 34 * 3. All advertising materials mentioning features or use of this software 35 * must display the following acknowledgement: 36 * 37 * This product includes software developed by Intel Corporation and 38 * its contributors. 39 * 40 * 4. Neither the name of Intel Corporation or its contributors may be 41 * used to endorse or promote products derived from this software 42 * without specific prior written permission. 43 * 44 * THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION AND CONTRIBUTORS ``AS IS'' 45 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 47 * ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR CONTRIBUTORS BE 48 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 49 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 50 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 51 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 52 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 53 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 54 * THE POSSIBILITY OF SUCH DAMAGE. 55 * 56 */ 57 58#include <sys/types.h> 59 60#include <netinet/in.h> 61#include <arpa/nameser.h> 62 63#include <errno.h> 64#include <resolv.h> 65#include <string.h> 66 67/* Data. */ 68 69static char digits[] = "0123456789"; 70 71/* Forward. */ 72 73static int special(int); 74static int printable(int); 75static int dn_find(const u_char *, const u_char *, 76 const u_char * const *, 77 const u_char * const *); 78 79/* Public. */ 80 81/* 82 * ns_name_ntop(src, dst, dstsiz) 83 * Convert an encoded domain name to printable ascii as per RFC1035. 84 * return: 85 * Number of bytes written to buffer, or -1 (with errno set) 86 * notes: 87 * The root is returned as "." 88 * All other domains are returned in non absolute form 89 */ 90int 91ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) { 92 const u_char *cp; 93 char *dn, *eom; 94 u_char c; 95 u_int n; 96 97 cp = src; 98 dn = dst; 99 eom = dst + dstsiz; 100 101 while ((n = *cp++) != 0) { 102 if ((n & NS_CMPRSFLGS) != 0) { 103 /* Some kind of compression pointer. */ 104 errno = EMSGSIZE; 105 return (-1); 106 } 107 if (dn != dst) { 108 if (dn >= eom) { 109 errno = EMSGSIZE; 110 return (-1); 111 } 112 *dn++ = '.'; 113 } 114 if (dn + n >= eom) { 115 errno = EMSGSIZE; 116 return (-1); 117 } 118 for ((void)NULL; n > 0; n--) { 119 c = *cp++; 120 if (special(c)) { 121 if (dn + 1 >= eom) { 122 errno = EMSGSIZE; 123 return (-1); 124 } 125 *dn++ = '\\'; 126 *dn++ = (char)c; 127 } else if (!printable(c)) { 128 if (dn + 3 >= eom) { 129 errno = EMSGSIZE; 130 return (-1); 131 } 132 *dn++ = '\\'; 133 *dn++ = digits[c / 100]; 134 *dn++ = digits[(c % 100) / 10]; 135 *dn++ = digits[c % 10]; 136 } else { 137 if (dn >= eom) { 138 errno = EMSGSIZE; 139 return (-1); 140 } 141 *dn++ = (char)c; 142 } 143 } 144 } 145 if (dn == dst) { 146 if (dn >= eom) { 147 errno = EMSGSIZE; 148 return (-1); 149 } 150 *dn++ = '.'; 151 } 152 if (dn >= eom) { 153 errno = EMSGSIZE; 154 return (-1); 155 } 156 *dn++ = '\0'; 157 return ((int)(dn - dst)); 158} 159 160/* 161 * ns_name_pton(src, dst, dstsiz) 162 * Convert a ascii string into an encoded domain name as per RFC1035. 163 * return: 164 * -1 if it fails 165 * 1 if string was fully qualified 166 * 0 is string was not fully qualified 167 * notes: 168 * Enforces label and domain length limits. 169 */ 170 171int 172ns_name_pton(const char *src, u_char *dst, size_t dstsiz) { 173 u_char *label, *bp, *eom; 174 int c, n, escaped; 175 char *cp; 176 177 escaped = 0; 178 bp = dst; 179 eom = dst + dstsiz; 180 label = bp++; 181 182 while ((c = *src++) != 0) { 183 if (escaped) { 184 if ((cp = strchr(digits, c)) != NULL) { 185 n = (int)(cp - digits) * 100; 186 if ((c = *src++) == 0 || 187 (cp = strchr(digits, c)) == NULL) { 188 errno = EMSGSIZE; 189 return (-1); 190 } 191 n += (int)(cp - digits) * 10; 192 if ((c = *src++) == 0 || 193 (cp = strchr(digits, c)) == NULL) { 194 errno = EMSGSIZE; 195 return (-1); 196 } 197 n += (int)(cp - digits); 198 if (n > 255) { 199 errno = EMSGSIZE; 200 return (-1); 201 } 202 c = n; 203 } 204 escaped = 0; 205 } else if (c == '\\') { 206 escaped = 1; 207 continue; 208 } else if (c == '.') { 209 c = ((int)(bp - label) - 1); 210 if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ 211 errno = EMSGSIZE; 212 return (-1); 213 } 214 if (label >= eom) { 215 errno = EMSGSIZE; 216 return (-1); 217 } 218 *label = (u_char)c; 219 /* Fully qualified ? */ 220 if (*src == '\0') { 221 if (c != 0) { 222 if (bp >= eom) { 223 errno = EMSGSIZE; 224 return (-1); 225 } 226 *bp++ = '\0'; 227 } 228 if ((bp - dst) > MAXCDNAME) { 229 errno = EMSGSIZE; 230 return (-1); 231 } 232 return (1); 233 } 234 if (c == 0) { 235 errno = EMSGSIZE; 236 return (-1); 237 } 238 label = bp++; 239 continue; 240 } 241 if (bp >= eom) { 242 errno = EMSGSIZE; 243 return (-1); 244 } 245 *bp++ = (u_char)c; 246 } 247 c = ((int)(bp - label) - 1); 248 if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ 249 errno = EMSGSIZE; 250 return (-1); 251 } 252 if (label >= eom) { 253 errno = EMSGSIZE; 254 return (-1); 255 } 256 *label = (u_char)c; 257 if (c != 0) { 258 if (bp >= eom) { 259 errno = EMSGSIZE; 260 return (-1); 261 } 262 *bp++ = 0; 263 } 264 if ((bp - dst) > MAXCDNAME) { /* src too big */ 265 errno = EMSGSIZE; 266 return (-1); 267 } 268 return (0); 269} 270 271/* 272 * ns_name_unpack(msg, eom, src, dst, dstsiz) 273 * Unpack a domain name from a message, source may be compressed. 274 * return: 275 * -1 if it fails, or consumed octets if it succeeds. 276 */ 277int 278ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, 279 u_char *dst, size_t dstsiz) 280{ 281 const u_char *srcp, *dstlim; 282 u_char *dstp; 283 int n, len, checked; 284 285 len = -1; 286 checked = 0; 287 dstp = dst; 288 srcp = src; 289 dstlim = dst + dstsiz; 290 if (srcp < msg || srcp >= eom) { 291 errno = EMSGSIZE; 292 return (-1); 293 } 294 /* Fetch next label in domain name. */ 295 while ((n = *srcp++) != 0) { 296 /* Check for indirection. */ 297 switch (n & NS_CMPRSFLGS) { 298 case 0: 299 /* Limit checks. */ 300 if (dstp + n + 1 >= dstlim || srcp + n >= eom) { 301 errno = EMSGSIZE; 302 return (-1); 303 } 304 checked += n + 1; 305 *dstp++ = (u_char)n; 306 memcpy(dstp, srcp, n); 307 dstp += n; 308 srcp += n; 309 break; 310 311 case NS_CMPRSFLGS: 312 if (srcp >= eom) { 313 errno = EMSGSIZE; 314 return (-1); 315 } 316 if (len < 0) 317 len = (int)(srcp - src) + 1; 318 srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff)); 319 if (srcp < msg || srcp >= eom) { /* Out of range. */ 320 errno = EMSGSIZE; 321 return (-1); 322 } 323 checked += 2; 324 /* 325 * Check for loops in the compressed name; 326 * if we've looked at the whole message, 327 * there must be a loop. 328 */ 329 if (checked >= eom - msg) { 330 errno = EMSGSIZE; 331 return (-1); 332 } 333 break; 334 335 default: 336 errno = EMSGSIZE; 337 return (-1); /* flag error */ 338 } 339 } 340 *dstp = '\0'; 341 if (len < 0) 342 len = (int)(srcp - src); 343 return (len); 344} 345 346/* 347 * ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr) 348 * Pack domain name 'domain' into 'comp_dn'. 349 * return: 350 * Size of the compressed name, or -1. 351 * notes: 352 * 'dnptrs' is an array of pointers to previous compressed names. 353 * dnptrs[0] is a pointer to the beginning of the message. The array 354 * ends with NULL. 355 * 'lastdnptr' is a pointer to the end of the array pointed to 356 * by 'dnptrs'. 357 * Side effects: 358 * The list of pointers in dnptrs is updated for labels inserted into 359 * the message as we compress the name. If 'dnptr' is NULL, we don't 360 * try to compress names. If 'lastdnptr' is NULL, we don't update the 361 * list. 362 */ 363int 364ns_name_pack(const u_char *src, u_char *dst, int dstsiz, 365 const u_char **dnptrs, const u_char **lastdnptr) 366{ 367 u_char *dstp; 368 const u_char **cpp, **lpp, *eob, *msg; 369 const u_char *srcp; 370 int n, l; 371 372 srcp = src; 373 dstp = dst; 374 eob = dstp + dstsiz; 375 lpp = cpp = NULL; 376 if (dnptrs != NULL) { 377 if ((msg = *dnptrs++) != NULL) { 378 for (cpp = dnptrs; *cpp != NULL; cpp++) 379 (void)NULL; 380 lpp = cpp; /* end of list to search */ 381 } 382 } else 383 msg = NULL; 384 385 /* make sure the domain we are about to add is legal */ 386 l = 0; 387 do { 388 n = *srcp; 389 if ((n & NS_CMPRSFLGS) != 0) { 390 errno = EMSGSIZE; 391 return (-1); 392 } 393 l += n + 1; 394 if (l > MAXCDNAME) { 395 errno = EMSGSIZE; 396 return (-1); 397 } 398 srcp += n + 1; 399 } while (n != 0); 400 401 srcp = src; 402 do { 403 /* Look to see if we can use pointers. */ 404 n = *srcp; 405 if (n != 0 && msg != NULL) { 406 l = dn_find(srcp, msg, (const u_char * const *)dnptrs, 407 (const u_char * const *)lpp); 408 if (l >= 0) { 409 if (dstp + 1 >= eob) { 410 errno = EMSGSIZE; 411 return (-1); 412 } 413 *dstp++ = (u_char)((l >> 8) | NS_CMPRSFLGS ); 414 *dstp++ = (u_char)( l % 256 ); 415 return ((int)(dstp - dst)); 416 } 417 /* Not found, save it. */ 418 if (lastdnptr != NULL && cpp < lastdnptr - 1 && 419 (dstp - msg) < 0x4000) { 420 *cpp++ = dstp; 421 *cpp = NULL; 422 } 423 } 424 /* copy label to buffer */ 425 if (n & NS_CMPRSFLGS) { /* Should not happen. */ 426 errno = EMSGSIZE; 427 return (-1); 428 } 429 if (dstp + 1 + n >= eob) { 430 errno = EMSGSIZE; 431 return (-1); 432 } 433 memcpy(dstp, srcp, n + 1); 434 srcp += n + 1; 435 dstp += n + 1; 436 } while (n != 0); 437 438 if (dstp > eob) { 439 if (msg != NULL) 440 *lpp = NULL; 441 errno = EMSGSIZE; 442 return (-1); 443 } 444 return ((int)(dstp - dst)); 445} 446 447/* 448 * ns_name_uncompress(msg, eom, src, dst, dstsiz) 449 * Expand compressed domain name to presentation format. 450 * return: 451 * Number of bytes read out of `src', or -1 (with errno set). 452 * note: 453 * Root domain returns as "." not "". 454 */ 455int 456ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src, 457 char *dst, size_t dstsiz) 458{ 459 u_char tmp[NS_MAXCDNAME]; 460 int n; 461 462 if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1) 463 return (-1); 464 if (ns_name_ntop(tmp, dst, dstsiz) == -1) 465 return (-1); 466 return (n); 467} 468 469/* 470 * ns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr) 471 * Compress a domain name into wire format, using compression pointers. 472 * return: 473 * Number of bytes consumed in `dst' or -1 (with errno set). 474 * notes: 475 * 'dnptrs' is an array of pointers to previous compressed names. 476 * dnptrs[0] is a pointer to the beginning of the message. 477 * The list ends with NULL. 'lastdnptr' is a pointer to the end of the 478 * array pointed to by 'dnptrs'. Side effect is to update the list of 479 * pointers for labels inserted into the message as we compress the name. 480 * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' 481 * is NULL, we don't update the list. 482 */ 483int 484ns_name_compress(const char *src, u_char *dst, size_t dstsiz, 485 const u_char **dnptrs, const u_char **lastdnptr) 486{ 487 u_char tmp[NS_MAXCDNAME]; 488 489 if (ns_name_pton(src, tmp, sizeof tmp) == -1) 490 return (-1); 491 return (ns_name_pack(tmp, dst, (int)dstsiz, dnptrs, lastdnptr)); 492} 493 494/* 495 * ns_name_skip(ptrptr, eom) 496 * Advance *ptrptr to skip over the compressed name it points at. 497 * return: 498 * 0 on success, -1 (with errno set) on failure. 499 */ 500int 501ns_name_skip(const u_char **ptrptr, const u_char *eom) { 502 const u_char *cp; 503 u_int n; 504 505 cp = *ptrptr; 506 while (cp < eom && (n = *cp++) != 0) { 507 /* Check for indirection. */ 508 switch (n & NS_CMPRSFLGS) { 509 case 0: /* normal case, n == len */ 510 cp += n; 511 continue; 512 case NS_CMPRSFLGS: /* indirection */ 513 cp++; 514 break; 515 default: /* illegal type */ 516 errno = EMSGSIZE; 517 return (-1); 518 } 519 break; 520 } 521 if (cp > eom) { 522 errno = EMSGSIZE; 523 return (-1); 524 } 525 *ptrptr = cp; 526 return (0); 527} 528 529/* Private. */ 530 531/* 532 * special(ch) 533 * Thinking in noninternationalized USASCII (per the DNS spec), 534 * is this characted special ("in need of quoting") ? 535 * return: 536 * boolean. 537 */ 538static int 539special(int ch) { 540 switch (ch) { 541 case 0x22: /* '"' */ 542 case 0x2E: /* '.' */ 543 case 0x3B: /* ';' */ 544 case 0x5C: /* '\\' */ 545 /* Special modifiers in zone files. */ 546 case 0x40: /* '@' */ 547 case 0x24: /* '$' */ 548 return (1); 549 default: 550 return (0); 551 } 552} 553 554/* 555 * printable(ch) 556 * Thinking in noninternationalized USASCII (per the DNS spec), 557 * is this character visible and not a space when printed ? 558 * return: 559 * boolean. 560 */ 561static int 562printable(int ch) { 563 return (ch > 0x20 && ch < 0x7f); 564} 565 566/* 567 * Thinking in noninternationalized USASCII (per the DNS spec), 568 * convert this character to lower case if it's upper case. 569 */ 570static int 571mklower(int ch) { 572 if (ch >= 0x41 && ch <= 0x5A) 573 return (ch + 0x20); 574 return (ch); 575} 576 577/* 578 * dn_find(domain, msg, dnptrs, lastdnptr) 579 * Search for the counted-label name in an array of compressed names. 580 * return: 581 * offset from msg if found, or -1. 582 * notes: 583 * dnptrs is the pointer to the first name on the list, 584 * not the pointer to the start of the message. 585 */ 586static int 587dn_find(const u_char *domain, const u_char *msg, 588 const u_char * const *dnptrs, 589 const u_char * const *lastdnptr) 590{ 591 const u_char *dn, *cp, *sp; 592 const u_char * const *cpp; 593 u_int n; 594 595 for (cpp = dnptrs; cpp < lastdnptr; cpp++) { 596 dn = domain; 597 sp = cp = *cpp; 598 while ((n = *cp++) != 0) { 599 /* 600 * check for indirection 601 */ 602 switch (n & NS_CMPRSFLGS) { 603 case 0: /* normal case, n == len */ 604 if (n != *dn++) 605 goto next; 606 for ((void)NULL; n > 0; n--) 607 if (mklower(*dn++) != mklower(*cp++)) 608 goto next; 609 /* Is next root for both ? */ 610 if (*dn == '\0' && *cp == '\0') 611 return ((int)(sp - msg)); 612 if (*dn) 613 continue; 614 goto next; 615 616 case NS_CMPRSFLGS: /* indirection */ 617 cp = msg + (((n & 0x3f) << 8) | *cp); 618 break; 619 620 default: /* illegal type */ 621 errno = EMSGSIZE; 622 return (-1); 623 } 624 } 625 next: ; 626 } 627 errno = ENOENT; 628 return (-1); 629} 630