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