1156952Sume/* 2156952Sume * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 3156952Sume * Copyright (c) 1996,1999 by Internet Software Consortium. 4156952Sume * 5156952Sume * Permission to use, copy, modify, and distribute this software for any 6156952Sume * purpose with or without fee is hereby granted, provided that the above 7156952Sume * copyright notice and this permission notice appear in all copies. 8156952Sume * 9156952Sume * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 10156952Sume * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11156952Sume * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 12156952Sume * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13156952Sume * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14156952Sume * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 15156952Sume * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16156952Sume */ 17156952Sume 18156952Sume#ifndef lint 19269867Sumestatic const char rcsid[] = "$Id: ns_name.c,v 1.11 2009/01/23 19:59:16 each Exp $"; 20156952Sume#endif 21269867Sume#include <sys/cdefs.h> 22269867Sume__FBSDID("$FreeBSD: stable/11/lib/libc/nameser/ns_name.c 352027 2019-09-08 01:58:02Z cy $"); 23156952Sume 24156952Sume#include "port_before.h" 25156952Sume 26156952Sume#include <sys/types.h> 27156952Sume 28156952Sume#include <netinet/in.h> 29156952Sume#include <arpa/nameser.h> 30156952Sume 31156952Sume#include <errno.h> 32156952Sume#include <resolv.h> 33156952Sume#include <string.h> 34156952Sume#include <ctype.h> 35156952Sume#include <stdlib.h> 36156952Sume#include <limits.h> 37156952Sume 38156952Sume#include "port_after.h" 39156952Sume 40156952Sume#ifdef SPRINTF_CHAR 41156952Sume# define SPRINTF(x) strlen(sprintf/**/x) 42156952Sume#else 43156952Sume# define SPRINTF(x) ((size_t)sprintf x) 44156952Sume#endif 45156952Sume 46170242Sume#define NS_TYPE_ELT 0x40 /*%< EDNS0 extended label type */ 47156952Sume#define DNS_LABELTYPE_BITSTRING 0x41 48156952Sume 49156952Sume/* Data. */ 50156952Sume 51156952Sumestatic const char digits[] = "0123456789"; 52156952Sume 53156952Sumestatic const char digitvalue[256] = { 54156952Sume -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/ 55156952Sume -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/ 56156952Sume -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/ 57156952Sume 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/ 58156952Sume -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/ 59156952Sume -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/ 60156952Sume -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/ 61156952Sume -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/ 62156952Sume -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 63156952Sume -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 64156952Sume -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 65156952Sume -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 66156952Sume -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 67156952Sume -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 68156952Sume -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 69156952Sume -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/ 70156952Sume}; 71156952Sume 72156952Sume/* Forward. */ 73156952Sume 74156952Sumestatic int special(int); 75156952Sumestatic int printable(int); 76156952Sumestatic int dn_find(const u_char *, const u_char *, 77156952Sume const u_char * const *, 78156952Sume const u_char * const *); 79156952Sumestatic int encode_bitsring(const char **, const char *, 80156952Sume unsigned char **, unsigned char **, 81156952Sume unsigned const char *); 82156952Sumestatic int labellen(const u_char *); 83156952Sumestatic int decode_bitstring(const unsigned char **, 84156952Sume char *, const char *); 85156952Sume 86156952Sume/* Public. */ 87156952Sume 88170242Sume/*% 89156952Sume * Convert an encoded domain name to printable ascii as per RFC1035. 90170242Sume 91156952Sume * return: 92170242Sume *\li Number of bytes written to buffer, or -1 (with errno set) 93170242Sume * 94156952Sume * notes: 95170242Sume *\li The root is returned as "." 96170242Sume *\li All other domains are returned in non absolute form 97156952Sume */ 98156952Sumeint 99156952Sumens_name_ntop(const u_char *src, char *dst, size_t dstsiz) 100156952Sume{ 101156952Sume const u_char *cp; 102156952Sume char *dn, *eom; 103156952Sume u_char c; 104156952Sume u_int n; 105156952Sume int l; 106156952Sume 107156952Sume cp = src; 108156952Sume dn = dst; 109156952Sume eom = dst + dstsiz; 110156952Sume 111156952Sume while ((n = *cp++) != 0) { 112156952Sume if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { 113156952Sume /* Some kind of compression pointer. */ 114156952Sume errno = EMSGSIZE; 115156952Sume return (-1); 116156952Sume } 117156952Sume if (dn != dst) { 118156952Sume if (dn >= eom) { 119156952Sume errno = EMSGSIZE; 120156952Sume return (-1); 121156952Sume } 122156952Sume *dn++ = '.'; 123156952Sume } 124156952Sume if ((l = labellen(cp - 1)) < 0) { 125170242Sume errno = EMSGSIZE; /*%< XXX */ 126269867Sume return (-1); 127156952Sume } 128156952Sume if (dn + l >= eom) { 129156952Sume errno = EMSGSIZE; 130156952Sume return (-1); 131156952Sume } 132156952Sume if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) { 133156952Sume int m; 134156952Sume 135156952Sume if (n != DNS_LABELTYPE_BITSTRING) { 136156952Sume /* XXX: labellen should reject this case */ 137156952Sume errno = EINVAL; 138269867Sume return (-1); 139156952Sume } 140156952Sume if ((m = decode_bitstring(&cp, dn, eom)) < 0) 141156952Sume { 142156952Sume errno = EMSGSIZE; 143269867Sume return (-1); 144156952Sume } 145156952Sume dn += m; 146156952Sume continue; 147156952Sume } 148156952Sume for ((void)NULL; l > 0; l--) { 149156952Sume c = *cp++; 150156952Sume if (special(c)) { 151156952Sume if (dn + 1 >= eom) { 152156952Sume errno = EMSGSIZE; 153156952Sume return (-1); 154156952Sume } 155156952Sume *dn++ = '\\'; 156156952Sume *dn++ = (char)c; 157156952Sume } else if (!printable(c)) { 158156952Sume if (dn + 3 >= eom) { 159156952Sume errno = EMSGSIZE; 160156952Sume return (-1); 161156952Sume } 162156952Sume *dn++ = '\\'; 163156952Sume *dn++ = digits[c / 100]; 164156952Sume *dn++ = digits[(c % 100) / 10]; 165156952Sume *dn++ = digits[c % 10]; 166156952Sume } else { 167156952Sume if (dn >= eom) { 168156952Sume errno = EMSGSIZE; 169156952Sume return (-1); 170156952Sume } 171156952Sume *dn++ = (char)c; 172156952Sume } 173156952Sume } 174156952Sume } 175156952Sume if (dn == dst) { 176156952Sume if (dn >= eom) { 177156952Sume errno = EMSGSIZE; 178156952Sume return (-1); 179156952Sume } 180156952Sume *dn++ = '.'; 181156952Sume } 182156952Sume if (dn >= eom) { 183156952Sume errno = EMSGSIZE; 184156952Sume return (-1); 185156952Sume } 186156952Sume *dn++ = '\0'; 187156952Sume return (dn - dst); 188156952Sume} 189156952Sume 190170242Sume/*% 191156952Sume * Convert a ascii string into an encoded domain name as per RFC1035. 192170242Sume * 193156952Sume * return: 194170242Sume * 195170242Sume *\li -1 if it fails 196170242Sume *\li 1 if string was fully qualified 197170242Sume *\li 0 is string was not fully qualified 198170242Sume * 199156952Sume * notes: 200170242Sume *\li Enforces label and domain length limits. 201156952Sume */ 202269867Sumeint 203269867Sumens_name_pton(const char *src, u_char *dst, size_t dstsiz) { 204269867Sume return (ns_name_pton2(src, dst, dstsiz, NULL)); 205269867Sume} 206156952Sume 207269867Sume/* 208269867Sume * ns_name_pton2(src, dst, dstsiz, *dstlen) 209269867Sume * Convert a ascii string into an encoded domain name as per RFC1035. 210269867Sume * return: 211269867Sume * -1 if it fails 212269867Sume * 1 if string was fully qualified 213269867Sume * 0 is string was not fully qualified 214269867Sume * side effects: 215269867Sume * fills in *dstlen (if non-NULL) 216269867Sume * notes: 217269867Sume * Enforces label and domain length limits. 218269867Sume */ 219156952Sumeint 220269867Sumens_name_pton2(const char *src, u_char *dst, size_t dstsiz, size_t *dstlen) { 221156952Sume u_char *label, *bp, *eom; 222156952Sume int c, n, escaped, e = 0; 223156952Sume char *cp; 224156952Sume 225156952Sume escaped = 0; 226156952Sume bp = dst; 227156952Sume eom = dst + dstsiz; 228156952Sume label = bp++; 229156952Sume 230156952Sume while ((c = *src++) != 0) { 231156952Sume if (escaped) { 232170242Sume if (c == '[') { /*%< start a bit string label */ 233156952Sume if ((cp = strchr(src, ']')) == NULL) { 234170242Sume errno = EINVAL; /*%< ??? */ 235269867Sume return (-1); 236156952Sume } 237156952Sume if ((e = encode_bitsring(&src, cp + 2, 238156952Sume &label, &bp, eom)) 239156952Sume != 0) { 240156952Sume errno = e; 241269867Sume return (-1); 242156952Sume } 243156952Sume escaped = 0; 244156952Sume label = bp++; 245156952Sume if ((c = *src++) == 0) 246156952Sume goto done; 247156952Sume else if (c != '.') { 248156952Sume errno = EINVAL; 249269867Sume return (-1); 250156952Sume } 251156952Sume continue; 252156952Sume } 253156952Sume else if ((cp = strchr(digits, c)) != NULL) { 254156952Sume n = (cp - digits) * 100; 255156952Sume if ((c = *src++) == 0 || 256156952Sume (cp = strchr(digits, c)) == NULL) { 257156952Sume errno = EMSGSIZE; 258156952Sume return (-1); 259156952Sume } 260156952Sume n += (cp - digits) * 10; 261156952Sume if ((c = *src++) == 0 || 262156952Sume (cp = strchr(digits, c)) == NULL) { 263156952Sume errno = EMSGSIZE; 264156952Sume return (-1); 265156952Sume } 266156952Sume n += (cp - digits); 267156952Sume if (n > 255) { 268156952Sume errno = EMSGSIZE; 269156952Sume return (-1); 270156952Sume } 271156952Sume c = n; 272156952Sume } 273156952Sume escaped = 0; 274156952Sume } else if (c == '\\') { 275156952Sume escaped = 1; 276156952Sume continue; 277156952Sume } else if (c == '.') { 278156952Sume c = (bp - label - 1); 279170242Sume if ((c & NS_CMPRSFLGS) != 0) { /*%< Label too big. */ 280156952Sume errno = EMSGSIZE; 281156952Sume return (-1); 282156952Sume } 283156952Sume if (label >= eom) { 284156952Sume errno = EMSGSIZE; 285156952Sume return (-1); 286156952Sume } 287156952Sume *label = c; 288156952Sume /* Fully qualified ? */ 289156952Sume if (*src == '\0') { 290156952Sume if (c != 0) { 291156952Sume if (bp >= eom) { 292156952Sume errno = EMSGSIZE; 293156952Sume return (-1); 294156952Sume } 295156952Sume *bp++ = '\0'; 296156952Sume } 297156952Sume if ((bp - dst) > MAXCDNAME) { 298156952Sume errno = EMSGSIZE; 299156952Sume return (-1); 300156952Sume } 301269867Sume if (dstlen != NULL) 302269867Sume *dstlen = (bp - dst); 303156952Sume return (1); 304156952Sume } 305156952Sume if (c == 0 || *src == '.') { 306156952Sume errno = EMSGSIZE; 307156952Sume return (-1); 308156952Sume } 309156952Sume label = bp++; 310156952Sume continue; 311156952Sume } 312156952Sume if (bp >= eom) { 313156952Sume errno = EMSGSIZE; 314156952Sume return (-1); 315156952Sume } 316156952Sume *bp++ = (u_char)c; 317156952Sume } 318156952Sume c = (bp - label - 1); 319170242Sume if ((c & NS_CMPRSFLGS) != 0) { /*%< Label too big. */ 320156952Sume errno = EMSGSIZE; 321156952Sume return (-1); 322156952Sume } 323156952Sume done: 324156952Sume if (label >= eom) { 325156952Sume errno = EMSGSIZE; 326156952Sume return (-1); 327156952Sume } 328156952Sume *label = c; 329156952Sume if (c != 0) { 330156952Sume if (bp >= eom) { 331156952Sume errno = EMSGSIZE; 332156952Sume return (-1); 333156952Sume } 334156952Sume *bp++ = 0; 335156952Sume } 336170242Sume if ((bp - dst) > MAXCDNAME) { /*%< src too big */ 337156952Sume errno = EMSGSIZE; 338156952Sume return (-1); 339156952Sume } 340269867Sume if (dstlen != NULL) 341269867Sume *dstlen = (bp - dst); 342156952Sume return (0); 343156952Sume} 344156952Sume 345170242Sume/*% 346156952Sume * Convert a network strings labels into all lowercase. 347170242Sume * 348156952Sume * return: 349170242Sume *\li Number of bytes written to buffer, or -1 (with errno set) 350170242Sume * 351156952Sume * notes: 352170242Sume *\li Enforces label and domain length limits. 353156952Sume */ 354156952Sume 355156952Sumeint 356156952Sumens_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) 357156952Sume{ 358156952Sume const u_char *cp; 359156952Sume u_char *dn, *eom; 360156952Sume u_char c; 361156952Sume u_int n; 362156952Sume int l; 363156952Sume 364156952Sume cp = src; 365156952Sume dn = dst; 366156952Sume eom = dst + dstsiz; 367156952Sume 368156952Sume if (dn >= eom) { 369156952Sume errno = EMSGSIZE; 370156952Sume return (-1); 371156952Sume } 372156952Sume while ((n = *cp++) != 0) { 373156952Sume if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { 374156952Sume /* Some kind of compression pointer. */ 375156952Sume errno = EMSGSIZE; 376156952Sume return (-1); 377156952Sume } 378156952Sume *dn++ = n; 379156952Sume if ((l = labellen(cp - 1)) < 0) { 380156952Sume errno = EMSGSIZE; 381156952Sume return (-1); 382156952Sume } 383156952Sume if (dn + l >= eom) { 384156952Sume errno = EMSGSIZE; 385156952Sume return (-1); 386156952Sume } 387156952Sume for ((void)NULL; l > 0; l--) { 388156952Sume c = *cp++; 389269867Sume if (isascii(c) && isupper(c)) 390156952Sume *dn++ = tolower(c); 391156952Sume else 392156952Sume *dn++ = c; 393156952Sume } 394156952Sume } 395156952Sume *dn++ = '\0'; 396156952Sume return (dn - dst); 397156952Sume} 398156952Sume 399170242Sume/*% 400156952Sume * Unpack a domain name from a message, source may be compressed. 401170242Sume * 402156952Sume * return: 403170242Sume *\li -1 if it fails, or consumed octets if it succeeds. 404156952Sume */ 405156952Sumeint 406156952Sumens_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, 407156952Sume u_char *dst, size_t dstsiz) 408156952Sume{ 409269867Sume return (ns_name_unpack2(msg, eom, src, dst, dstsiz, NULL)); 410269867Sume} 411269867Sume 412269867Sume/* 413269867Sume * ns_name_unpack2(msg, eom, src, dst, dstsiz, *dstlen) 414269867Sume * Unpack a domain name from a message, source may be compressed. 415269867Sume * return: 416269867Sume * -1 if it fails, or consumed octets if it succeeds. 417269867Sume * side effect: 418269867Sume * fills in *dstlen (if non-NULL). 419269867Sume */ 420269867Sumeint 421269867Sumens_name_unpack2(const u_char *msg, const u_char *eom, const u_char *src, 422269867Sume u_char *dst, size_t dstsiz, size_t *dstlen) 423269867Sume{ 424156952Sume const u_char *srcp, *dstlim; 425156952Sume u_char *dstp; 426156952Sume int n, len, checked, l; 427156952Sume 428156952Sume len = -1; 429156952Sume checked = 0; 430156952Sume dstp = dst; 431156952Sume srcp = src; 432156952Sume dstlim = dst + dstsiz; 433156952Sume if (srcp < msg || srcp >= eom) { 434156952Sume errno = EMSGSIZE; 435156952Sume return (-1); 436156952Sume } 437156952Sume /* Fetch next label in domain name. */ 438156952Sume while ((n = *srcp++) != 0) { 439156952Sume /* Check for indirection. */ 440156952Sume switch (n & NS_CMPRSFLGS) { 441156952Sume case 0: 442156952Sume case NS_TYPE_ELT: 443156952Sume /* Limit checks. */ 444156952Sume if ((l = labellen(srcp - 1)) < 0) { 445156952Sume errno = EMSGSIZE; 446269867Sume return (-1); 447156952Sume } 448156952Sume if (dstp + l + 1 >= dstlim || srcp + l >= eom) { 449156952Sume errno = EMSGSIZE; 450156952Sume return (-1); 451156952Sume } 452156952Sume checked += l + 1; 453156952Sume *dstp++ = n; 454156952Sume memcpy(dstp, srcp, l); 455156952Sume dstp += l; 456156952Sume srcp += l; 457156952Sume break; 458156952Sume 459156952Sume case NS_CMPRSFLGS: 460156952Sume if (srcp >= eom) { 461156952Sume errno = EMSGSIZE; 462156952Sume return (-1); 463156952Sume } 464156952Sume if (len < 0) 465156952Sume len = srcp - src + 1; 466269873Sume l = ((n & 0x3f) << 8) | (*srcp & 0xff); 467269873Sume if (l >= eom - msg) { /*%< Out of range. */ 468156952Sume errno = EMSGSIZE; 469156952Sume return (-1); 470156952Sume } 471269873Sume srcp = msg + l; 472156952Sume checked += 2; 473156952Sume /* 474156952Sume * Check for loops in the compressed name; 475156952Sume * if we've looked at the whole message, 476156952Sume * there must be a loop. 477156952Sume */ 478156952Sume if (checked >= eom - msg) { 479156952Sume errno = EMSGSIZE; 480156952Sume return (-1); 481156952Sume } 482156952Sume break; 483156952Sume 484156952Sume default: 485156952Sume errno = EMSGSIZE; 486170242Sume return (-1); /*%< flag error */ 487156952Sume } 488156952Sume } 489269867Sume *dstp++ = 0; 490269867Sume if (dstlen != NULL) 491269867Sume *dstlen = dstp - dst; 492156952Sume if (len < 0) 493156952Sume len = srcp - src; 494156952Sume return (len); 495156952Sume} 496156952Sume 497170242Sume/*% 498156952Sume * Pack domain name 'domain' into 'comp_dn'. 499170242Sume * 500156952Sume * return: 501170242Sume *\li Size of the compressed name, or -1. 502170242Sume * 503156952Sume * notes: 504170242Sume *\li 'dnptrs' is an array of pointers to previous compressed names. 505170242Sume *\li dnptrs[0] is a pointer to the beginning of the message. The array 506156952Sume * ends with NULL. 507170242Sume *\li 'lastdnptr' is a pointer to the end of the array pointed to 508156952Sume * by 'dnptrs'. 509170242Sume * 510156952Sume * Side effects: 511170242Sume *\li The list of pointers in dnptrs is updated for labels inserted into 512156952Sume * the message as we compress the name. If 'dnptr' is NULL, we don't 513156952Sume * try to compress names. If 'lastdnptr' is NULL, we don't update the 514156952Sume * list. 515156952Sume */ 516156952Sumeint 517156952Sumens_name_pack(const u_char *src, u_char *dst, int dstsiz, 518156952Sume const u_char **dnptrs, const u_char **lastdnptr) 519156952Sume{ 520156952Sume u_char *dstp; 521156952Sume const u_char **cpp, **lpp, *eob, *msg; 522156952Sume const u_char *srcp; 523156952Sume int n, l, first = 1; 524156952Sume 525156952Sume srcp = src; 526156952Sume dstp = dst; 527156952Sume eob = dstp + dstsiz; 528156952Sume lpp = cpp = NULL; 529156952Sume if (dnptrs != NULL) { 530156952Sume if ((msg = *dnptrs++) != NULL) { 531156952Sume for (cpp = dnptrs; *cpp != NULL; cpp++) 532156952Sume (void)NULL; 533170242Sume lpp = cpp; /*%< end of list to search */ 534156952Sume } 535156952Sume } else 536156952Sume msg = NULL; 537156952Sume 538156952Sume /* make sure the domain we are about to add is legal */ 539156952Sume l = 0; 540156952Sume do { 541156952Sume int l0; 542156952Sume 543156952Sume n = *srcp; 544156952Sume if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { 545156952Sume errno = EMSGSIZE; 546156952Sume return (-1); 547156952Sume } 548156952Sume if ((l0 = labellen(srcp)) < 0) { 549156952Sume errno = EINVAL; 550269867Sume return (-1); 551156952Sume } 552156952Sume l += l0 + 1; 553156952Sume if (l > MAXCDNAME) { 554156952Sume errno = EMSGSIZE; 555156952Sume return (-1); 556156952Sume } 557156952Sume srcp += l0 + 1; 558156952Sume } while (n != 0); 559156952Sume 560156952Sume /* from here on we need to reset compression pointer array on error */ 561156952Sume srcp = src; 562156952Sume do { 563156952Sume /* Look to see if we can use pointers. */ 564156952Sume n = *srcp; 565156952Sume if (n != 0 && msg != NULL) { 566156952Sume l = dn_find(srcp, msg, (const u_char * const *)dnptrs, 567156952Sume (const u_char * const *)lpp); 568156952Sume if (l >= 0) { 569156952Sume if (dstp + 1 >= eob) { 570156952Sume goto cleanup; 571156952Sume } 572156952Sume *dstp++ = (l >> 8) | NS_CMPRSFLGS; 573156952Sume *dstp++ = l % 256; 574156952Sume return (dstp - dst); 575156952Sume } 576156952Sume /* Not found, save it. */ 577156952Sume if (lastdnptr != NULL && cpp < lastdnptr - 1 && 578156952Sume (dstp - msg) < 0x4000 && first) { 579156952Sume *cpp++ = dstp; 580156952Sume *cpp = NULL; 581156952Sume first = 0; 582156952Sume } 583156952Sume } 584156952Sume /* copy label to buffer */ 585156952Sume if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { 586156952Sume /* Should not happen. */ 587156952Sume goto cleanup; 588156952Sume } 589156952Sume n = labellen(srcp); 590156952Sume if (dstp + 1 + n >= eob) { 591156952Sume goto cleanup; 592156952Sume } 593156952Sume memcpy(dstp, srcp, n + 1); 594156952Sume srcp += n + 1; 595156952Sume dstp += n + 1; 596156952Sume } while (n != 0); 597156952Sume 598156952Sume if (dstp > eob) { 599156952Sumecleanup: 600156952Sume if (msg != NULL) 601156952Sume *lpp = NULL; 602156952Sume errno = EMSGSIZE; 603156952Sume return (-1); 604156952Sume } 605156952Sume return (dstp - dst); 606156952Sume} 607156952Sume 608170242Sume/*% 609156952Sume * Expand compressed domain name to presentation format. 610170242Sume * 611156952Sume * return: 612170242Sume *\li Number of bytes read out of `src', or -1 (with errno set). 613170242Sume * 614156952Sume * note: 615170242Sume *\li Root domain returns as "." not "". 616156952Sume */ 617156952Sumeint 618156952Sumens_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src, 619156952Sume char *dst, size_t dstsiz) 620156952Sume{ 621156952Sume u_char tmp[NS_MAXCDNAME]; 622156952Sume int n; 623156952Sume 624156952Sume if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1) 625156952Sume return (-1); 626156952Sume if (ns_name_ntop(tmp, dst, dstsiz) == -1) 627156952Sume return (-1); 628156952Sume return (n); 629156952Sume} 630156952Sume 631170242Sume/*% 632156952Sume * Compress a domain name into wire format, using compression pointers. 633170242Sume * 634156952Sume * return: 635170242Sume *\li Number of bytes consumed in `dst' or -1 (with errno set). 636170242Sume * 637156952Sume * notes: 638170242Sume *\li 'dnptrs' is an array of pointers to previous compressed names. 639170242Sume *\li dnptrs[0] is a pointer to the beginning of the message. 640170242Sume *\li The list ends with NULL. 'lastdnptr' is a pointer to the end of the 641156952Sume * array pointed to by 'dnptrs'. Side effect is to update the list of 642156952Sume * pointers for labels inserted into the message as we compress the name. 643170242Sume *\li If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' 644156952Sume * is NULL, we don't update the list. 645156952Sume */ 646156952Sumeint 647156952Sumens_name_compress(const char *src, u_char *dst, size_t dstsiz, 648156952Sume const u_char **dnptrs, const u_char **lastdnptr) 649156952Sume{ 650156952Sume u_char tmp[NS_MAXCDNAME]; 651156952Sume 652156952Sume if (ns_name_pton(src, tmp, sizeof tmp) == -1) 653156952Sume return (-1); 654156952Sume return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr)); 655156952Sume} 656156952Sume 657170242Sume/*% 658156952Sume * Reset dnptrs so that there are no active references to pointers at or 659156952Sume * after src. 660156952Sume */ 661156952Sumevoid 662156952Sumens_name_rollback(const u_char *src, const u_char **dnptrs, 663156952Sume const u_char **lastdnptr) 664156952Sume{ 665156952Sume while (dnptrs < lastdnptr && *dnptrs != NULL) { 666156952Sume if (*dnptrs >= src) { 667156952Sume *dnptrs = NULL; 668156952Sume break; 669156952Sume } 670156952Sume dnptrs++; 671156952Sume } 672156952Sume} 673156952Sume 674170242Sume/*% 675156952Sume * Advance *ptrptr to skip over the compressed name it points at. 676170242Sume * 677156952Sume * return: 678170242Sume *\li 0 on success, -1 (with errno set) on failure. 679156952Sume */ 680156952Sumeint 681156952Sumens_name_skip(const u_char **ptrptr, const u_char *eom) 682156952Sume{ 683156952Sume const u_char *cp; 684156952Sume u_int n; 685352027Scy int l = 0; 686156952Sume 687156952Sume cp = *ptrptr; 688156952Sume while (cp < eom && (n = *cp++) != 0) { 689156952Sume /* Check for indirection. */ 690156952Sume switch (n & NS_CMPRSFLGS) { 691170242Sume case 0: /*%< normal case, n == len */ 692156952Sume cp += n; 693156952Sume continue; 694170242Sume case NS_TYPE_ELT: /*%< EDNS0 extended label */ 695352027Scy if (cp < eom && (l = labellen(cp - 1)) < 0) { 696170242Sume errno = EMSGSIZE; /*%< XXX */ 697269867Sume return (-1); 698156952Sume } 699156952Sume cp += l; 700156952Sume continue; 701170242Sume case NS_CMPRSFLGS: /*%< indirection */ 702156952Sume cp++; 703156952Sume break; 704170242Sume default: /*%< illegal type */ 705156952Sume errno = EMSGSIZE; 706156952Sume return (-1); 707156952Sume } 708156952Sume break; 709156952Sume } 710156952Sume if (cp > eom) { 711156952Sume errno = EMSGSIZE; 712156952Sume return (-1); 713156952Sume } 714156952Sume *ptrptr = cp; 715156952Sume return (0); 716156952Sume} 717156952Sume 718269867Sume/* Find the number of octets an nname takes up, including the root label. 719269867Sume * (This is basically ns_name_skip() without compression-pointer support.) 720269867Sume * ((NOTE: can only return zero if passed-in namesiz argument is zero.)) 721269867Sume */ 722269867Sumessize_t 723269867Sumens_name_length(ns_nname_ct nname, size_t namesiz) { 724269867Sume ns_nname_ct orig = nname; 725269867Sume u_int n; 726269867Sume 727269867Sume while (namesiz-- > 0 && (n = *nname++) != 0) { 728269867Sume if ((n & NS_CMPRSFLGS) != 0) { 729269867Sume errno = EISDIR; 730269867Sume return (-1); 731269867Sume } 732269867Sume if (n > namesiz) { 733269867Sume errno = EMSGSIZE; 734269867Sume return (-1); 735269867Sume } 736269867Sume nname += n; 737269867Sume namesiz -= n; 738269867Sume } 739269867Sume return (nname - orig); 740269867Sume} 741269867Sume 742269867Sume/* Compare two nname's for equality. Return -1 on error (setting errno). 743269867Sume */ 744269867Sumeint 745269867Sumens_name_eq(ns_nname_ct a, size_t as, ns_nname_ct b, size_t bs) { 746269867Sume ns_nname_ct ae = a + as, be = b + bs; 747269867Sume int ac, bc; 748269867Sume 749269867Sume while (ac = *a, bc = *b, ac != 0 && bc != 0) { 750269867Sume if ((ac & NS_CMPRSFLGS) != 0 || (bc & NS_CMPRSFLGS) != 0) { 751269867Sume errno = EISDIR; 752269867Sume return (-1); 753269867Sume } 754269867Sume if (a + ac >= ae || b + bc >= be) { 755269867Sume errno = EMSGSIZE; 756269867Sume return (-1); 757269867Sume } 758269867Sume if (ac != bc || strncasecmp((const char *) ++a, 759269867Sume (const char *) ++b, ac) != 0) 760269867Sume return (0); 761269867Sume a += ac, b += bc; 762269867Sume } 763269867Sume return (ac == 0 && bc == 0); 764269867Sume} 765269867Sume 766269867Sume/* Is domain "A" owned by (at or below) domain "B"? 767269867Sume */ 768269867Sumeint 769269867Sumens_name_owned(ns_namemap_ct a, int an, ns_namemap_ct b, int bn) { 770269867Sume /* If A is shorter, it cannot be owned by B. */ 771269867Sume if (an < bn) 772269867Sume return (0); 773269867Sume 774269867Sume /* If they are unequal before the length of the shorter, A cannot... */ 775269867Sume while (bn > 0) { 776269867Sume if (a->len != b->len || 777269867Sume strncasecmp((const char *) a->base, 778269867Sume (const char *) b->base, a->len) != 0) 779269867Sume return (0); 780269867Sume a++, an--; 781269867Sume b++, bn--; 782269867Sume } 783269867Sume 784269867Sume /* A might be longer or not, but either way, B owns it. */ 785269867Sume return (1); 786269867Sume} 787269867Sume 788269867Sume/* Build an array of <base,len> tuples from an nname, top-down order. 789269867Sume * Return the number of tuples (labels) thus discovered. 790269867Sume */ 791269867Sumeint 792269867Sumens_name_map(ns_nname_ct nname, size_t namelen, ns_namemap_t map, int mapsize) { 793269867Sume u_int n; 794269867Sume int l; 795269867Sume 796269867Sume n = *nname++; 797269867Sume namelen--; 798269867Sume 799269867Sume /* Root zone? */ 800269867Sume if (n == 0) { 801269867Sume /* Extra data follows name? */ 802269867Sume if (namelen > 0) { 803269867Sume errno = EMSGSIZE; 804269867Sume return (-1); 805269867Sume } 806269867Sume return (0); 807269867Sume } 808269867Sume 809269867Sume /* Compression pointer? */ 810269867Sume if ((n & NS_CMPRSFLGS) != 0) { 811269867Sume errno = EISDIR; 812269867Sume return (-1); 813269867Sume } 814269867Sume 815269867Sume /* Label too long? */ 816269867Sume if (n > namelen) { 817269867Sume errno = EMSGSIZE; 818269867Sume return (-1); 819269867Sume } 820269867Sume 821269867Sume /* Recurse to get rest of name done first. */ 822269867Sume l = ns_name_map(nname + n, namelen - n, map, mapsize); 823269867Sume if (l < 0) 824269867Sume return (-1); 825269867Sume 826269867Sume /* Too many labels? */ 827269867Sume if (l >= mapsize) { 828269867Sume errno = ENAMETOOLONG; 829269867Sume return (-1); 830269867Sume } 831269867Sume 832269867Sume /* We're on our way back up-stack, store current map data. */ 833269867Sume map[l].base = nname; 834269867Sume map[l].len = n; 835269867Sume return (l + 1); 836269867Sume} 837269867Sume 838269867Sume/* Count the labels in a domain name. Root counts, so COM. has two. This 839269867Sume * is to make the result comparable to the result of ns_name_map(). 840269867Sume */ 841269867Sumeint 842269867Sumens_name_labels(ns_nname_ct nname, size_t namesiz) { 843269867Sume int ret = 0; 844269867Sume u_int n; 845269867Sume 846269867Sume while (namesiz-- > 0 && (n = *nname++) != 0) { 847269867Sume if ((n & NS_CMPRSFLGS) != 0) { 848269867Sume errno = EISDIR; 849269867Sume return (-1); 850269867Sume } 851269867Sume if (n > namesiz) { 852269867Sume errno = EMSGSIZE; 853269867Sume return (-1); 854269867Sume } 855269867Sume nname += n; 856269867Sume namesiz -= n; 857269867Sume ret++; 858269867Sume } 859269867Sume return (ret + 1); 860269867Sume} 861269867Sume 862156952Sume/* Private. */ 863156952Sume 864170242Sume/*% 865156952Sume * Thinking in noninternationalized USASCII (per the DNS spec), 866156952Sume * is this characted special ("in need of quoting") ? 867170242Sume * 868156952Sume * return: 869170242Sume *\li boolean. 870156952Sume */ 871156952Sumestatic int 872156952Sumespecial(int ch) { 873156952Sume switch (ch) { 874170242Sume case 0x22: /*%< '"' */ 875170242Sume case 0x2E: /*%< '.' */ 876170242Sume case 0x3B: /*%< ';' */ 877170242Sume case 0x5C: /*%< '\\' */ 878170242Sume case 0x28: /*%< '(' */ 879170242Sume case 0x29: /*%< ')' */ 880156952Sume /* Special modifiers in zone files. */ 881170242Sume case 0x40: /*%< '@' */ 882170242Sume case 0x24: /*%< '$' */ 883156952Sume return (1); 884156952Sume default: 885156952Sume return (0); 886156952Sume } 887156952Sume} 888156952Sume 889170242Sume/*% 890156952Sume * Thinking in noninternationalized USASCII (per the DNS spec), 891156952Sume * is this character visible and not a space when printed ? 892170242Sume * 893156952Sume * return: 894170242Sume *\li boolean. 895156952Sume */ 896156952Sumestatic int 897156952Sumeprintable(int ch) { 898156952Sume return (ch > 0x20 && ch < 0x7f); 899156952Sume} 900156952Sume 901170242Sume/*% 902156952Sume * Thinking in noninternationalized USASCII (per the DNS spec), 903156952Sume * convert this character to lower case if it's upper case. 904156952Sume */ 905156952Sumestatic int 906156952Sumemklower(int ch) { 907156952Sume if (ch >= 0x41 && ch <= 0x5A) 908156952Sume return (ch + 0x20); 909156952Sume return (ch); 910156952Sume} 911156952Sume 912170242Sume/*% 913156952Sume * Search for the counted-label name in an array of compressed names. 914170242Sume * 915156952Sume * return: 916170242Sume *\li offset from msg if found, or -1. 917170242Sume * 918156952Sume * notes: 919170242Sume *\li dnptrs is the pointer to the first name on the list, 920170242Sume *\li not the pointer to the start of the message. 921156952Sume */ 922156952Sumestatic int 923156952Sumedn_find(const u_char *domain, const u_char *msg, 924156952Sume const u_char * const *dnptrs, 925156952Sume const u_char * const *lastdnptr) 926156952Sume{ 927156952Sume const u_char *dn, *cp, *sp; 928156952Sume const u_char * const *cpp; 929156952Sume u_int n; 930156952Sume 931156952Sume for (cpp = dnptrs; cpp < lastdnptr; cpp++) { 932156952Sume sp = *cpp; 933156952Sume /* 934156952Sume * terminate search on: 935156952Sume * root label 936156952Sume * compression pointer 937156952Sume * unusable offset 938156952Sume */ 939156952Sume while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 && 940156952Sume (sp - msg) < 0x4000) { 941156952Sume dn = domain; 942156952Sume cp = sp; 943156952Sume while ((n = *cp++) != 0) { 944156952Sume /* 945156952Sume * check for indirection 946156952Sume */ 947156952Sume switch (n & NS_CMPRSFLGS) { 948170242Sume case 0: /*%< normal case, n == len */ 949170242Sume n = labellen(cp - 1); /*%< XXX */ 950156952Sume if (n != *dn++) 951156952Sume goto next; 952156952Sume 953156952Sume for ((void)NULL; n > 0; n--) 954156952Sume if (mklower(*dn++) != 955156952Sume mklower(*cp++)) 956156952Sume goto next; 957156952Sume /* Is next root for both ? */ 958156952Sume if (*dn == '\0' && *cp == '\0') 959156952Sume return (sp - msg); 960156952Sume if (*dn) 961156952Sume continue; 962156952Sume goto next; 963170242Sume case NS_CMPRSFLGS: /*%< indirection */ 964156952Sume cp = msg + (((n & 0x3f) << 8) | *cp); 965156952Sume break; 966156952Sume 967170242Sume default: /*%< illegal type */ 968156952Sume errno = EMSGSIZE; 969156952Sume return (-1); 970156952Sume } 971156952Sume } 972156952Sume next: ; 973156952Sume sp += *sp + 1; 974156952Sume } 975156952Sume } 976156952Sume errno = ENOENT; 977156952Sume return (-1); 978156952Sume} 979156952Sume 980156952Sumestatic int 981156952Sumedecode_bitstring(const unsigned char **cpp, char *dn, const char *eom) 982156952Sume{ 983156952Sume const unsigned char *cp = *cpp; 984156952Sume char *beg = dn, tc; 985156952Sume int b, blen, plen, i; 986156952Sume 987156952Sume if ((blen = (*cp & 0xff)) == 0) 988156952Sume blen = 256; 989156952Sume plen = (blen + 3) / 4; 990156952Sume plen += sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1); 991156952Sume if (dn + plen >= eom) 992269867Sume return (-1); 993156952Sume 994156952Sume cp++; 995156952Sume i = SPRINTF((dn, "\\[x")); 996156952Sume if (i < 0) 997156952Sume return (-1); 998156952Sume dn += i; 999156952Sume for (b = blen; b > 7; b -= 8, cp++) { 1000156952Sume i = SPRINTF((dn, "%02x", *cp & 0xff)); 1001156952Sume if (i < 0) 1002156952Sume return (-1); 1003156952Sume dn += i; 1004156952Sume } 1005156952Sume if (b > 4) { 1006156952Sume tc = *cp++; 1007156952Sume i = SPRINTF((dn, "%02x", tc & (0xff << (8 - b)))); 1008156952Sume if (i < 0) 1009156952Sume return (-1); 1010156952Sume dn += i; 1011156952Sume } else if (b > 0) { 1012156952Sume tc = *cp++; 1013156952Sume i = SPRINTF((dn, "%1x", 1014156952Sume ((tc >> 4) & 0x0f) & (0x0f << (4 - b)))); 1015156952Sume if (i < 0) 1016156952Sume return (-1); 1017156952Sume dn += i; 1018156952Sume } 1019156952Sume i = SPRINTF((dn, "/%d]", blen)); 1020156952Sume if (i < 0) 1021156952Sume return (-1); 1022156952Sume dn += i; 1023156952Sume 1024156952Sume *cpp = cp; 1025269867Sume return (dn - beg); 1026156952Sume} 1027156952Sume 1028156952Sumestatic int 1029156952Sumeencode_bitsring(const char **bp, const char *end, unsigned char **labelp, 1030269867Sume unsigned char ** dst, unsigned const char *eom) 1031156952Sume{ 1032156952Sume int afterslash = 0; 1033156952Sume const char *cp = *bp; 1034156952Sume unsigned char *tp; 1035156952Sume char c; 1036156952Sume const char *beg_blen; 1037156952Sume char *end_blen = NULL; 1038156952Sume int value = 0, count = 0, tbcount = 0, blen = 0; 1039156952Sume 1040156952Sume beg_blen = end_blen = NULL; 1041156952Sume 1042156952Sume /* a bitstring must contain at least 2 characters */ 1043156952Sume if (end - cp < 2) 1044269867Sume return (EINVAL); 1045156952Sume 1046156952Sume /* XXX: currently, only hex strings are supported */ 1047156952Sume if (*cp++ != 'x') 1048269867Sume return (EINVAL); 1049170242Sume if (!isxdigit((*cp) & 0xff)) /*%< reject '\[x/BLEN]' */ 1050269867Sume return (EINVAL); 1051156952Sume 1052156952Sume for (tp = *dst + 1; cp < end && tp < eom; cp++) { 1053156952Sume switch((c = *cp)) { 1054170242Sume case ']': /*%< end of the bitstring */ 1055156952Sume if (afterslash) { 1056156952Sume if (beg_blen == NULL) 1057269867Sume return (EINVAL); 1058156952Sume blen = (int)strtol(beg_blen, &end_blen, 10); 1059156952Sume if (*end_blen != ']') 1060269867Sume return (EINVAL); 1061156952Sume } 1062156952Sume if (count) 1063156952Sume *tp++ = ((value << 4) & 0xff); 1064170242Sume cp++; /*%< skip ']' */ 1065156952Sume goto done; 1066156952Sume case '/': 1067156952Sume afterslash = 1; 1068156952Sume break; 1069156952Sume default: 1070156952Sume if (afterslash) { 1071156952Sume if (!isdigit(c&0xff)) 1072269867Sume return (EINVAL); 1073156952Sume if (beg_blen == NULL) { 1074156952Sume 1075156952Sume if (c == '0') { 1076156952Sume /* blen never begings with 0 */ 1077269867Sume return (EINVAL); 1078156952Sume } 1079156952Sume beg_blen = cp; 1080156952Sume } 1081156952Sume } else { 1082156952Sume if (!isxdigit(c&0xff)) 1083269867Sume return (EINVAL); 1084156952Sume value <<= 4; 1085156952Sume value += digitvalue[(int)c]; 1086156952Sume count += 4; 1087156952Sume tbcount += 4; 1088156952Sume if (tbcount > 256) 1089269867Sume return (EINVAL); 1090156952Sume if (count == 8) { 1091156952Sume *tp++ = value; 1092156952Sume count = 0; 1093156952Sume } 1094156952Sume } 1095156952Sume break; 1096156952Sume } 1097156952Sume } 1098156952Sume done: 1099156952Sume if (cp >= end || tp >= eom) 1100269867Sume return (EMSGSIZE); 1101156952Sume 1102156952Sume /* 1103156952Sume * bit length validation: 1104156952Sume * If a <length> is present, the number of digits in the <bit-data> 1105156952Sume * MUST be just sufficient to contain the number of bits specified 1106156952Sume * by the <length>. If there are insignificant bits in a final 1107156952Sume * hexadecimal or octal digit, they MUST be zero. 1108170242Sume * RFC2673, Section 3.2. 1109156952Sume */ 1110156952Sume if (blen > 0) { 1111156952Sume int traillen; 1112156952Sume 1113156952Sume if (((blen + 3) & ~3) != tbcount) 1114269867Sume return (EINVAL); 1115170242Sume traillen = tbcount - blen; /*%< between 0 and 3 */ 1116156952Sume if (((value << (8 - traillen)) & 0xff) != 0) 1117269867Sume return (EINVAL); 1118156952Sume } 1119156952Sume else 1120156952Sume blen = tbcount; 1121156952Sume if (blen == 256) 1122156952Sume blen = 0; 1123156952Sume 1124156952Sume /* encode the type and the significant bit fields */ 1125156952Sume **labelp = DNS_LABELTYPE_BITSTRING; 1126156952Sume **dst = blen; 1127156952Sume 1128156952Sume *bp = cp; 1129156952Sume *dst = tp; 1130156952Sume 1131269867Sume return (0); 1132156952Sume} 1133156952Sume 1134156952Sumestatic int 1135156952Sumelabellen(const u_char *lp) 1136156952Sume{ 1137156952Sume int bitlen; 1138156952Sume u_char l = *lp; 1139156952Sume 1140156952Sume if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) { 1141156952Sume /* should be avoided by the caller */ 1142269867Sume return (-1); 1143156952Sume } 1144156952Sume 1145156952Sume if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) { 1146156952Sume if (l == DNS_LABELTYPE_BITSTRING) { 1147156952Sume if ((bitlen = *(lp + 1)) == 0) 1148156952Sume bitlen = 256; 1149269867Sume return ((bitlen + 7 ) / 8 + 1); 1150156952Sume } 1151269867Sume return (-1); /*%< unknwon ELT */ 1152156952Sume } 1153269867Sume return (l); 1154156952Sume} 1155170242Sume 1156170242Sume/*! \file */ 1157