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