Update.
[glibc.git] / resolv / ns_name.c
blobb96a1fe8ab58679822258e12b660a0e2d9d628cf
1 /*
2 * Copyright (c) 1996,1999 by Internet Software Consortium.
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.
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.
18 #if !defined(_LIBC) && !defined(lint)
19 static const char rcsid[] = "$BINDId: ns_name.c,v 8.15 2000/03/30 22:53:46 vixie Exp $";
20 #endif
22 #include <sys/types.h>
24 #include <netinet/in.h>
25 #include <arpa/nameser.h>
27 #include <ctype.h>
28 #include <errno.h>
29 #include <resolv.h>
30 #include <string.h>
31 #include <ctype.h>
33 /* Data. */
35 static const char digits[] = "0123456789";
37 /* Forward. */
39 static int special(int);
40 static int printable(int);
41 static int dn_find(const u_char *, const u_char *,
42 const u_char * const *,
43 const u_char * const *);
45 /* Public. */
48 * ns_name_ntop(src, dst, dstsiz)
49 * Convert an encoded domain name to printable ascii as per RFC1035.
50 * return:
51 * Number of bytes written to buffer, or -1 (with errno set)
52 * notes:
53 * The root is returned as "."
54 * All other domains are returned in non absolute form
56 int
57 ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) {
58 const u_char *cp;
59 char *dn, *eom;
60 u_char c;
61 u_int n;
63 cp = src;
64 dn = dst;
65 eom = dst + dstsiz;
67 while ((n = *cp++) != 0) {
68 if ((n & NS_CMPRSFLGS) != 0 && n != 0x41) {
69 /* Some kind of compression pointer. */
70 __set_errno (EMSGSIZE);
71 return (-1);
73 if (dn != dst) {
74 if (dn >= eom) {
75 __set_errno (EMSGSIZE);
76 return (-1);
78 *dn++ = '.';
81 if (n == 0x41) {
82 n = *cp++ / 8;
83 if (dn + n * 2 + 4 >= eom) {
84 __set_errno (EMSGSIZE);
85 return (-1);
87 *dn++ = '\\';
88 *dn++ = '[';
89 *dn++ = 'x';
91 while (n-- > 0) {
92 c = *cp++;
93 unsigned u = c >> 4;
94 *dn++ = u > 9 ? 'a' + u - 10 : '0' + u;
95 u = c & 0xf;
96 *dn++ = u > 9 ? 'a' + u - 10 : '0' + u;
99 *dn++ = ']';
100 continue;
103 if (dn + n >= eom) {
104 __set_errno (EMSGSIZE);
105 return (-1);
107 for ((void)NULL; n > 0; n--) {
108 c = *cp++;
109 if (special(c)) {
110 if (dn + 1 >= eom) {
111 __set_errno (EMSGSIZE);
112 return (-1);
114 *dn++ = '\\';
115 *dn++ = (char)c;
116 } else if (!printable(c)) {
117 if (dn + 3 >= eom) {
118 __set_errno (EMSGSIZE);
119 return (-1);
121 *dn++ = '\\';
122 *dn++ = digits[c / 100];
123 *dn++ = digits[(c % 100) / 10];
124 *dn++ = digits[c % 10];
125 } else {
126 if (dn >= eom) {
127 __set_errno (EMSGSIZE);
128 return (-1);
130 *dn++ = (char)c;
134 if (dn == dst) {
135 if (dn >= eom) {
136 __set_errno (EMSGSIZE);
137 return (-1);
139 *dn++ = '.';
141 if (dn >= eom) {
142 __set_errno (EMSGSIZE);
143 return (-1);
145 *dn++ = '\0';
146 return (dn - dst);
150 * ns_name_pton(src, dst, dstsiz)
151 * Convert a ascii string into an encoded domain name as per RFC1035.
152 * return:
153 * -1 if it fails
154 * 1 if string was fully qualified
155 * 0 is string was not fully qualified
156 * notes:
157 * Enforces label and domain length limits.
161 ns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
162 u_char *label, *bp, *eom;
163 int c, n, escaped;
164 char *cp;
166 escaped = 0;
167 bp = dst;
168 eom = dst + dstsiz;
169 label = bp++;
171 while ((c = *src++) != 0) {
172 if (escaped) {
173 if ((cp = strchr(digits, c)) != NULL) {
174 n = (cp - digits) * 100;
175 if ((c = *src++) == 0 ||
176 (cp = strchr(digits, c)) == NULL) {
177 __set_errno (EMSGSIZE);
178 return (-1);
180 n += (cp - digits) * 10;
181 if ((c = *src++) == 0 ||
182 (cp = strchr(digits, c)) == NULL) {
183 __set_errno (EMSGSIZE);
184 return (-1);
186 n += (cp - digits);
187 if (n > 255) {
188 __set_errno (EMSGSIZE);
189 return (-1);
191 c = n;
192 } else if (c == '[' && label == bp - 1 && *src == 'x') {
193 /* Theoretically we would have to handle \[o
194 as well but we do not since we do not need
195 it internally. */
196 *label = 0x41;
197 label = bp++;
198 ++src;
199 while (isxdigit (*src)) {
200 n = *src > '9' ? *src - 'a' + 10 : *src - '0';
201 ++src;
202 if (! isxdigit(*src)) {
203 __set_errno (EMSGSIZE);
204 return (-1);
206 n <<= 4;
207 n += *src > '9' ? *src - 'a' + 10 : *src - '0';
208 if (bp + 1 >= eom) {
209 __set_errno (EMSGSIZE);
210 return (-1);
212 *bp++ = n;
213 ++src;
215 *label = (bp - label - 1) * 8;
216 if (*src++ != ']' || *src++ != '.') {
217 __set_errno (EMSGSIZE);
218 return (-1);
220 escaped = 0;
221 label = bp++;
222 if (bp >= eom) {
223 __set_errno (EMSGSIZE);
224 return (-1);
226 continue;
228 escaped = 0;
229 } else if (c == '\\') {
230 escaped = 1;
231 continue;
232 } else if (c == '.') {
233 c = (bp - label - 1);
234 if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */
235 __set_errno (EMSGSIZE);
236 return (-1);
238 if (label >= eom) {
239 __set_errno (EMSGSIZE);
240 return (-1);
242 *label = c;
243 /* Fully qualified ? */
244 if (*src == '\0') {
245 if (c != 0) {
246 if (bp >= eom) {
247 __set_errno (EMSGSIZE);
248 return (-1);
250 *bp++ = '\0';
252 if ((bp - dst) > MAXCDNAME) {
253 __set_errno (EMSGSIZE);
254 return (-1);
256 return (1);
258 if (c == 0 || *src == '.') {
259 __set_errno (EMSGSIZE);
260 return (-1);
262 label = bp++;
263 continue;
265 if (bp >= eom) {
266 __set_errno (EMSGSIZE);
267 return (-1);
269 *bp++ = (u_char)c;
271 c = (bp - label - 1);
272 if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */
273 __set_errno (EMSGSIZE);
274 return (-1);
276 if (label >= eom) {
277 __set_errno (EMSGSIZE);
278 return (-1);
280 *label = c;
281 if (c != 0) {
282 if (bp >= eom) {
283 __set_errno (EMSGSIZE);
284 return (-1);
286 *bp++ = 0;
288 if ((bp - dst) > MAXCDNAME) { /* src too big */
289 __set_errno (EMSGSIZE);
290 return (-1);
292 return (0);
296 * ns_name_ntol(src, dst, dstsiz)
297 * Convert a network strings labels into all lowercase.
298 * return:
299 * Number of bytes written to buffer, or -1 (with errno set)
300 * notes:
301 * Enforces label and domain length limits.
305 ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) {
306 const u_char *cp;
307 u_char *dn, *eom;
308 u_char c;
309 u_int n;
311 cp = src;
312 dn = dst;
313 eom = dst + dstsiz;
315 while ((n = *cp++) != 0) {
316 if ((n & NS_CMPRSFLGS) != 0) {
317 /* Some kind of compression pointer. */
318 __set_errno (EMSGSIZE);
319 return (-1);
321 *dn++ = n;
322 if (dn + n >= eom) {
323 __set_errno (EMSGSIZE);
324 return (-1);
326 for ((void)NULL; n > 0; n--) {
327 c = *cp++;
328 if (isupper(c))
329 *dn++ = tolower(c);
330 else
331 *dn++ = c;
334 *dn++ = '\0';
335 return (dn - dst);
339 * ns_name_unpack(msg, eom, src, dst, dstsiz)
340 * Unpack a domain name from a message, source may be compressed.
341 * return:
342 * -1 if it fails, or consumed octets if it succeeds.
345 ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
346 u_char *dst, size_t dstsiz)
348 const u_char *srcp, *dstlim;
349 u_char *dstp;
350 int n, len, checked;
352 len = -1;
353 checked = 0;
354 dstp = dst;
355 srcp = src;
356 dstlim = dst + dstsiz;
357 if (srcp < msg || srcp >= eom) {
358 __set_errno (EMSGSIZE);
359 return (-1);
361 /* Fetch next label in domain name. */
362 while ((n = *srcp++) != 0) {
363 /* Check for indirection. */
364 switch (n & NS_CMPRSFLGS) {
365 case 0x40:
366 if (n == 0x41) {
367 if (dstp + 1 >= dstlim) {
368 __set_errno (EMSGSIZE);
369 return (-1);
371 *dstp++ = 0x41;
372 n = *srcp++ / 8;
373 ++checked;
374 } else {
375 __set_errno (EMSGSIZE);
376 return (-1); /* flag error */
378 /* FALLTHROUGH */
379 case 0:
380 /* Limit checks. */
381 if (dstp + n + 1 >= dstlim || srcp + n >= eom) {
382 __set_errno (EMSGSIZE);
383 return (-1);
385 checked += n + 1;
386 dstp = mempcpy(dstp, srcp - 1, n + 1);
387 srcp += n;
388 break;
390 case NS_CMPRSFLGS:
391 if (srcp >= eom) {
392 __set_errno (EMSGSIZE);
393 return (-1);
395 if (len < 0)
396 len = srcp - src + 1;
397 srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
398 if (srcp < msg || srcp >= eom) { /* Out of range. */
399 __set_errno (EMSGSIZE);
400 return (-1);
402 checked += 2;
404 * Check for loops in the compressed name;
405 * if we've looked at the whole message,
406 * there must be a loop.
408 if (checked >= eom - msg) {
409 __set_errno (EMSGSIZE);
410 return (-1);
412 break;
414 default:
415 __set_errno (EMSGSIZE);
416 return (-1); /* flag error */
419 *dstp = '\0';
420 if (len < 0)
421 len = srcp - src;
422 return (len);
426 * ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr)
427 * Pack domain name 'domain' into 'comp_dn'.
428 * return:
429 * Size of the compressed name, or -1.
430 * notes:
431 * 'dnptrs' is an array of pointers to previous compressed names.
432 * dnptrs[0] is a pointer to the beginning of the message. The array
433 * ends with NULL.
434 * 'lastdnptr' is a pointer to the end of the array pointed to
435 * by 'dnptrs'.
436 * Side effects:
437 * The list of pointers in dnptrs is updated for labels inserted into
438 * the message as we compress the name. If 'dnptr' is NULL, we don't
439 * try to compress names. If 'lastdnptr' is NULL, we don't update the
440 * list.
443 ns_name_pack(const u_char *src, u_char *dst, int dstsiz,
444 const u_char **dnptrs, const u_char **lastdnptr)
446 u_char *dstp;
447 const u_char **cpp, **lpp, *eob, *msg;
448 const u_char *srcp;
449 int n, l, first = 1;
451 srcp = src;
452 dstp = dst;
453 eob = dstp + dstsiz;
454 lpp = cpp = NULL;
455 if (dnptrs != NULL) {
456 if ((msg = *dnptrs++) != NULL) {
457 for (cpp = dnptrs; *cpp != NULL; cpp++)
458 (void)NULL;
459 lpp = cpp; /* end of list to search */
461 } else
462 msg = NULL;
464 /* make sure the domain we are about to add is legal */
465 l = 0;
466 do {
467 n = *srcp;
468 if ((n & NS_CMPRSFLGS) != 0 && n != 0x41) {
469 __set_errno (EMSGSIZE);
470 return (-1);
472 if (n == 0x41)
473 n = *++srcp / 8;
474 l += n + 1;
475 if (l > MAXCDNAME) {
476 __set_errno (EMSGSIZE);
477 return (-1);
479 srcp += n + 1;
480 } while (n != 0);
482 /* from here on we need to reset compression pointer array on error */
483 srcp = src;
484 do {
485 /* Look to see if we can use pointers. */
486 n = *srcp;
487 if (n != 0 && n != 0x41 && msg != NULL) {
488 l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
489 (const u_char * const *)lpp);
490 if (l >= 0) {
491 if (dstp + 1 >= eob) {
492 goto cleanup;
494 *dstp++ = (l >> 8) | NS_CMPRSFLGS;
495 *dstp++ = l % 256;
496 return (dstp - dst);
498 /* Not found, save it. */
499 if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
500 (dstp - msg) < 0x4000 && first) {
501 *cpp++ = dstp;
502 *cpp = NULL;
503 first = 0;
506 /* copy label to buffer */
507 if ((n & NS_CMPRSFLGS) != 0 && n != 0x41) { /* Should not happen. */
508 goto cleanup;
510 if (n == 0x41) {
511 n = *++srcp / 8;
512 if (dstp + 1 >= eob)
513 goto cleanup;
514 *dstp++ = 0x41;
516 if (dstp + 1 + n >= eob) {
517 goto cleanup;
519 memcpy(dstp, srcp, n + 1);
520 srcp += n + 1;
521 dstp += n + 1;
522 } while (n != 0);
524 if (dstp > eob) {
525 cleanup:
526 if (msg != NULL)
527 *lpp = NULL;
528 __set_errno (EMSGSIZE);
529 return (-1);
531 return (dstp - dst);
535 * ns_name_uncompress(msg, eom, src, dst, dstsiz)
536 * Expand compressed domain name to presentation format.
537 * return:
538 * Number of bytes read out of `src', or -1 (with errno set).
539 * note:
540 * Root domain returns as "." not "".
543 ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
544 char *dst, size_t dstsiz)
546 u_char tmp[NS_MAXCDNAME];
547 int n;
549 if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
550 return (-1);
551 if (ns_name_ntop(tmp, dst, dstsiz) == -1)
552 return (-1);
553 return (n);
557 * ns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr)
558 * Compress a domain name into wire format, using compression pointers.
559 * return:
560 * Number of bytes consumed in `dst' or -1 (with errno set).
561 * notes:
562 * 'dnptrs' is an array of pointers to previous compressed names.
563 * dnptrs[0] is a pointer to the beginning of the message.
564 * The list ends with NULL. 'lastdnptr' is a pointer to the end of the
565 * array pointed to by 'dnptrs'. Side effect is to update the list of
566 * pointers for labels inserted into the message as we compress the name.
567 * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
568 * is NULL, we don't update the list.
571 ns_name_compress(const char *src, u_char *dst, size_t dstsiz,
572 const u_char **dnptrs, const u_char **lastdnptr)
574 u_char tmp[NS_MAXCDNAME];
576 if (ns_name_pton(src, tmp, sizeof tmp) == -1)
577 return (-1);
578 return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
582 * Reset dnptrs so that there are no active references to pointers at or
583 * after src.
585 void
586 ns_name_rollback(const u_char *src, const u_char **dnptrs,
587 const u_char **lastdnptr)
589 while (dnptrs < lastdnptr && *dnptrs != NULL) {
590 if (*dnptrs >= src) {
591 *dnptrs = NULL;
592 break;
594 dnptrs++;
599 * ns_name_skip(ptrptr, eom)
600 * Advance *ptrptr to skip over the compressed name it points at.
601 * return:
602 * 0 on success, -1 (with errno set) on failure.
605 ns_name_skip(const u_char **ptrptr, const u_char *eom) {
606 const u_char *cp;
607 u_int n;
609 cp = *ptrptr;
610 while (cp < eom && (n = *cp++) != 0) {
611 /* Check for indirection. */
612 switch (n & NS_CMPRSFLGS) {
613 case 0: /* normal case, n == len */
614 cp += n;
615 continue;
616 case NS_CMPRSFLGS: /* indirection */
617 cp++;
618 break;
619 default: /* illegal type */
620 __set_errno (EMSGSIZE);
621 return (-1);
623 break;
625 if (cp > eom) {
626 __set_errno (EMSGSIZE);
627 return (-1);
629 *ptrptr = cp;
630 return (0);
633 /* Private. */
636 * special(ch)
637 * Thinking in noninternationalized USASCII (per the DNS spec),
638 * is this characted special ("in need of quoting") ?
639 * return:
640 * boolean.
642 static int
643 special(int ch) {
644 switch (ch) {
645 case 0x22: /* '"' */
646 case 0x2E: /* '.' */
647 case 0x3B: /* ';' */
648 case 0x5C: /* '\\' */
649 /* Special modifiers in zone files. */
650 case 0x40: /* '@' */
651 case 0x24: /* '$' */
652 return (1);
653 default:
654 return (0);
659 * printable(ch)
660 * Thinking in noninternationalized USASCII (per the DNS spec),
661 * is this character visible and not a space when printed ?
662 * return:
663 * boolean.
665 static int
666 printable(int ch) {
667 return (ch > 0x20 && ch < 0x7f);
671 * Thinking in noninternationalized USASCII (per the DNS spec),
672 * convert this character to lower case if it's upper case.
674 static int
675 mklower(int ch) {
676 if (ch >= 0x41 && ch <= 0x5A)
677 return (ch + 0x20);
678 return (ch);
682 * dn_find(domain, msg, dnptrs, lastdnptr)
683 * Search for the counted-label name in an array of compressed names.
684 * return:
685 * offset from msg if found, or -1.
686 * notes:
687 * dnptrs is the pointer to the first name on the list,
688 * not the pointer to the start of the message.
690 static int
691 dn_find(const u_char *domain, const u_char *msg,
692 const u_char * const *dnptrs,
693 const u_char * const *lastdnptr)
695 const u_char *dn, *cp, *sp;
696 const u_char * const *cpp;
697 u_int n;
699 for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
700 sp = *cpp;
702 * terminate search on:
703 * root label
704 * compression pointer
705 * unusable offset
707 while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 &&
708 (sp - msg) < 0x4000) {
709 dn = domain;
710 cp = sp;
711 while ((n = *cp++) != 0) {
713 * check for indirection
715 switch (n & NS_CMPRSFLGS) {
716 case 0: /* normal case, n == len */
717 if (n != *dn++)
718 goto next;
719 for ((void)NULL; n > 0; n--)
720 if (mklower(*dn++) !=
721 mklower(*cp++))
722 goto next;
723 /* Is next root for both ? */
724 if (*dn == '\0' && *cp == '\0')
725 return (sp - msg);
726 if (*dn)
727 continue;
728 goto next;
730 case NS_CMPRSFLGS: /* indirection */
731 cp = msg + (((n & 0x3f) << 8) | *cp);
732 break;
734 default: /* illegal type */
735 __set_errno (EMSGSIZE);
736 return (-1);
739 next:
740 sp += *sp + 1;
743 __set_errno (ENOENT);
744 return (-1);