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