IPv6 support (hopefully with fewer future transition pains)
[ircu2.10.12-pk.git] / ircd / ircd_reslib.c
1 /*
2  * Copyright (c) 1985, 1993
3  *    The Regents of the University of California.  All rights reserved.
4  * 
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 /*
35  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
36  * 
37  * Permission to use, copy, modify, and distribute this software for any
38  * purpose with or without fee is hereby granted, provided that the above
39  * copyright notice and this permission notice appear in all copies, and that
40  * the name of Digital Equipment Corporation not be used in advertising or
41  * publicity pertaining to distribution of the document or software without
42  * specific, written prior permission.
43  * 
44  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
45  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
46  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
47  * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
48  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
49  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
50  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
51  * SOFTWARE.
52  */
53
54 /*
55  * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
56  *
57  * Permission to use, copy, modify, and distribute this software for any
58  * purpose with or without fee is hereby granted, provided that the above
59  * copyright notice and this permission notice appear in all copies.
60  *
61  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
62  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
63  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
64  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
65  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
66  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
67  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
68  * SOFTWARE.
69  */
70
71 /* Original copyright ISC as above. 
72  * Code modified specifically for ircd use from the following orginal files
73  * in bind ...
74  *
75  * res_comp.c
76  * ns_name.c
77  * ns_netint.c
78  * res_init.c
79  * 
80  * - Dianora
81  */
82
83 #include "ircd.h"
84 #include "res.h"
85 #include "ircd_reslib.h"
86 #include "ircd_defs.h"
87 #include "fileio.h"
88 #include "ircd_string.h"
89
90 #include <ctype.h>
91 #include <errno.h>
92 #include <string.h>
93 #include <stdlib.h>
94 #include <stdio.h>
95
96 #define NS_TYPE_ELT             0x40 /* EDNS0 extended label type */
97 #define DNS_LABELTYPE_BITSTRING 0x41
98 #define MAXLINE 128
99
100 /* $Id$ */
101
102 struct irc_sockaddr irc_nsaddr_list[IRCD_MAXNS];
103 int irc_nscount = 0;
104 char irc_domain[HOSTLEN + 1];
105
106 static const char digitvalue[256] = {
107   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/
108   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/
109   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/
110    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1, /*64*/
111   -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/
112   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/
113   -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/
114   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/
115   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
116   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
117   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
118   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
119   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
120   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
121   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
122   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/
123 };
124
125 static int parse_resvconf(void);
126 static void add_nameserver(char *arg);
127
128 static const char digits[] = "0123456789";
129 static int labellen(const unsigned char *lp);
130 static int special(int ch);
131 static int printable(int ch);
132 static int irc_decode_bitstring(const char **cpp, char *dn, const char *eom);
133 static int irc_ns_name_compress(const char *src, unsigned char *dst, size_t dstsiz,
134     const unsigned char **dnptrs, const unsigned char **lastdnptr);
135 static int irc_dn_find(const unsigned char *, const unsigned char *, const unsigned char * const *,
136                        const unsigned char * const *);
137 static int irc_encode_bitsring(const char **, const char *, unsigned char **, unsigned char **, 
138                                const char *);
139 static int mklower(int ch);
140   
141 int
142 irc_res_init(void)
143 {
144   irc_nscount = 0;
145   return(parse_resvconf());
146 }
147
148 /* parse_resvconf()
149  *
150  * inputs - NONE
151  * output - -1 if failure 0 if success
152  * side effects - fills in irc_nsaddr_list
153  */
154 static int
155 parse_resvconf(void)
156 {
157   char *p;
158   char *opt;
159   char *arg;
160   char input[MAXLINE];
161   FBFILE *file;
162
163   /* XXX "/etc/resolv.conf" should be from a define in setup.h perhaps
164    * for cygwin support etc. this hardcodes it to unix for now -db
165    */
166   if ((file = fbopen("/etc/resolv.conf", "r")) == NULL)
167     return(-1);
168
169   while (fbgets(input, MAXLINE, file) != NULL)
170   {
171     /* blow away any newline */
172     if ((p = strpbrk(input, "\r\n")) != NULL)
173       *p = '\0';
174
175     /* Ignore comment lines immediately */
176     if (*input == '#')
177       continue;
178
179     p = input;
180     /* skip until something thats not a space is seen */
181     while (IsSpace(*p))
182       p++;
183     /* if at this point, have a '\0' then continue */
184     if (*p == '\0')
185       continue;
186
187     /* skip until a space is found */
188     opt = input;
189     while (!IsSpace(*p))
190       if (*p++ == '\0')
191         continue;  /* no arguments?.. ignore this line */
192     /* blow away the space character */
193     *p++ = '\0';
194
195     /* skip these spaces that are before the argument */
196     while (IsSpace(*p))
197       p++;
198     /* Now arg should be right where p is pointing */
199     arg = p;
200     if ((p = strpbrk(arg, " \t")) != NULL)
201       *p = '\0';  /* take the first word */
202
203     if (strcasecmp(opt, "domain") == 0)
204       ircd_strncpy(irc_domain, arg, HOSTLEN);
205     else if (strcasecmp(opt, "nameserver") == 0)
206       add_nameserver(arg);
207   }
208
209   fbclose(file);
210   return(0);
211 }
212
213 /* add_nameserver()
214  *
215  * input        - either an IPV4 address in dotted quad
216  *                or an IPV6 address in : format
217  * output       - NONE
218  * side effects - entry in irc_nsaddr_list is filled in as needed
219  */
220 static void
221 add_nameserver(char *arg)
222 {
223   struct irc_sockaddr res;
224
225   /* Done max number of nameservers? */
226   if ((irc_nscount + 1) >= IRCD_MAXNS)
227     return;
228
229   /* Failure converting from numeric string? */
230   if (!ircd_aton(&res.addr, arg))
231       return;
232   res.port = 53;
233   memcpy(&irc_nsaddr_list[irc_nscount], &res, sizeof(irc_nsaddr_list[irc_nscount]));
234   irc_nscount++;
235 }
236
237 /*
238  * Expand compressed domain name 'comp_dn' to full domain name.
239  * 'msg' is a pointer to the begining of the message,
240  * 'eomorig' points to the first location after the message,
241  * 'exp_dn' is a pointer to a buffer of size 'length' for the result.
242  * Return size of compressed name or -1 if there was an error.
243  */
244 int
245 irc_dn_expand(const unsigned char *msg, const unsigned char *eom,
246               const unsigned char *src, char *dst, int dstsiz)
247 {
248   int n = irc_ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz);
249
250   if (n > 0 && dst[0] == '.')
251     dst[0] = '\0';
252   return(n);
253 }
254
255 /*
256  * irc_ns_name_uncompress(msg, eom, src, dst, dstsiz)
257  *      Expand compressed domain name to presentation format.
258  * return:
259  *      Number of bytes read out of `src', or -1 (with errno set).
260  * note:
261  *      Root domain returns as "." not "".
262  */
263 int
264 irc_ns_name_uncompress(const unsigned char *msg, const unsigned char *eom,
265                        const unsigned char *src, char *dst, size_t dstsiz)
266 {
267   unsigned char tmp[NS_MAXCDNAME];
268   int n;
269
270   if ((n = irc_ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
271     return(-1);
272   if (irc_ns_name_ntop((char*)tmp, (char*)dst, dstsiz) == -1)
273     return(-1);
274   return(n);
275 }
276 /*
277  * irc_ns_name_unpack(msg, eom, src, dst, dstsiz)
278  *      Unpack a domain name from a message, source may be compressed.
279  * return:
280  *      -1 if it fails, or consumed octets if it succeeds.
281  */
282 int
283 irc_ns_name_unpack(const unsigned char *msg, const unsigned char *eom,
284                    const unsigned char *src, unsigned char *dst,
285                    size_t dstsiz)
286 {
287         const unsigned char *srcp, *dstlim;
288         unsigned char *dstp;
289         int n, len, checked, l;
290
291         len = -1;
292         checked = 0;
293         dstp = dst;
294         srcp = src;
295         dstlim = dst + dstsiz;
296         if (srcp < msg || srcp >= eom) {
297                 errno = EMSGSIZE;
298                 return (-1);
299         }
300         /* Fetch next label in domain name. */
301         while ((n = *srcp++) != 0) {
302                 /* Check for indirection. */
303                 switch (n & NS_CMPRSFLGS) {
304                 case 0:
305                 case NS_TYPE_ELT:
306                         /* Limit checks. */
307                         if ((l = labellen(srcp - 1)) < 0) {
308                                 errno = EMSGSIZE;
309                                 return(-1);
310                         }
311                         if (dstp + l + 1 >= dstlim || srcp + l >= eom) {
312                                 errno = EMSGSIZE;
313                                 return (-1);
314                         }
315                         checked += l + 1;
316                         *dstp++ = n;
317                         memcpy(dstp, srcp, l);
318                         dstp += l;
319                         srcp += l;
320                         break;
321
322                 case NS_CMPRSFLGS:
323                         if (srcp >= eom) {
324                                 errno = EMSGSIZE;
325                                 return (-1);
326                         }
327                         if (len < 0)
328                                 len = srcp - src + 1;
329                         srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
330                         if (srcp < msg || srcp >= eom) {  /* Out of range. */
331                                 errno = EMSGSIZE;
332                                 return (-1);
333                         }
334                         checked += 2;
335                         /*
336                          * Check for loops in the compressed name;
337                          * if we've looked at the whole message,
338                          * there must be a loop.
339                          */
340                         if (checked >= eom - msg) {
341                                 errno = EMSGSIZE;
342                                 return (-1);
343                         }
344                         break;
345
346                 default:
347                         errno = EMSGSIZE;
348                         return (-1);                    /* flag error */
349                 }
350         }
351         *dstp = '\0';
352         if (len < 0)
353                 len = srcp - src;
354         return (len);
355 }
356
357 /*
358  * irc_ns_name_ntop(src, dst, dstsiz)
359  *      Convert an encoded domain name to printable ascii as per RFC1035.
360  * return:
361  *      Number of bytes written to buffer, or -1 (with errno set)
362  * notes:
363  *      The root is returned as "."
364  *      All other domains are returned in non absolute form
365  */
366 int
367 irc_ns_name_ntop(const char *src, char *dst, size_t dstsiz)
368 {
369         const char *cp;
370         char *dn, *eom;
371         unsigned char c;
372         unsigned int n;
373         int l;
374
375         cp = src;
376         dn = dst;
377         eom = dst + dstsiz;
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                 if (dn != dst) {
386                         if (dn >= eom) {
387                                 errno = EMSGSIZE;
388                                 return (-1);
389                         }
390                         *dn++ = '.';
391                 }
392                 if ((l = labellen((const unsigned char*)(cp - 1))) < 0) {
393                         errno = EMSGSIZE; /* XXX */
394                         return(-1);
395                 }
396                 if (dn + l >= eom) {
397                         errno = EMSGSIZE;
398                         return (-1);
399                 }
400                 if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) {
401                         int m;
402
403                         if (n != DNS_LABELTYPE_BITSTRING) {
404                                 /* XXX: labellen should reject this case */
405                                 errno = EINVAL;
406                                 return(-1);
407                         }
408                         if ((m = irc_decode_bitstring(&cp, dn, eom)) < 0)
409                         {
410                                 errno = EMSGSIZE;
411                                 return(-1);
412                         }
413                         dn += m; 
414                         continue;
415                 }
416                 for ((void)NULL; l > 0; l--) {
417                         c = *cp++;
418                         if (special(c)) {
419                                 if (dn + 1 >= eom) {
420                                         errno = EMSGSIZE;
421                                         return (-1);
422                                 }
423                                 *dn++ = '\\';
424                                 *dn++ = (char)c;
425                         } else if (!printable(c)) {
426                                 if (dn + 3 >= eom) {
427                                         errno = EMSGSIZE;
428                                         return (-1);
429                                 }
430                                 *dn++ = '\\';
431                                 *dn++ = digits[c / 100];
432                                 *dn++ = digits[(c % 100) / 10];
433                                 *dn++ = digits[c % 10];
434                         } else {
435                                 if (dn >= eom) {
436                                         errno = EMSGSIZE;
437                                         return (-1);
438                                 }
439                                 *dn++ = (char)c;
440                         }
441                 }
442         }
443         if (dn == dst) {
444                 if (dn >= eom) {
445                         errno = EMSGSIZE;
446                         return (-1);
447                 }
448                 *dn++ = '.';
449         }
450         if (dn >= eom) {
451                 errno = EMSGSIZE;
452                 return (-1);
453         }
454         *dn++ = '\0';
455         return (dn - dst);
456 }
457
458 /*
459  * Pack domain name 'exp_dn' in presentation form into 'comp_dn'.
460  * Return the size of the compressed name or -1.
461  * 'length' is the size of the array pointed to by 'comp_dn'.
462  */
463 int
464 irc_dn_comp(const char *src, unsigned char *dst, int dstsiz,
465             unsigned char **dnptrs, unsigned char **lastdnptr)
466 {
467   return(irc_ns_name_compress(src, dst, (size_t)dstsiz,
468                               (const unsigned char **)dnptrs,
469                               (const unsigned char **)lastdnptr));
470 }
471
472 /*
473  * Skip over a compressed domain name. Return the size or -1.
474  */
475 int
476 irc_dn_skipname(const unsigned char *ptr, const unsigned char *eom) {
477   const unsigned char *saveptr = ptr;
478
479   if (irc_ns_name_skip(&ptr, eom) == -1)
480     return(-1);
481   return(ptr - saveptr);
482 }
483
484 /*
485  * ns_name_skip(ptrptr, eom)
486  *      Advance *ptrptr to skip over the compressed name it points at.
487  * return:
488  *      0 on success, -1 (with errno set) on failure.
489  */
490 int
491 irc_ns_name_skip(const unsigned char **ptrptr, const unsigned char *eom)
492 {
493   const unsigned char *cp;
494   unsigned int n;
495   int l;
496
497   cp = *ptrptr;
498
499   while (cp < eom && (n = *cp++) != 0)
500   {
501     /* Check for indirection. */
502     switch (n & NS_CMPRSFLGS)
503     {
504       case 0: /* normal case, n == len */
505         cp += n;
506         continue;
507       case NS_TYPE_ELT: /* EDNS0 extended label */
508         if ((l = labellen(cp - 1)) < 0)
509         {
510           errno = EMSGSIZE; /* XXX */
511           return(-1);
512         }
513
514         cp += l;
515         continue;
516       case NS_CMPRSFLGS: /* indirection */
517         cp++;
518         break;
519       default: /* illegal type */
520         errno = EMSGSIZE;
521         return(-1);
522     }
523
524     break;
525   }
526
527   if (cp > eom)
528   {
529     errno = EMSGSIZE;
530     return (-1);
531   }
532
533   *ptrptr = cp;
534   return(0);
535 }
536
537 unsigned int
538 irc_ns_get16(const unsigned char *src)
539 {
540   unsigned int dst;
541
542   IRC_NS_GET16(dst, src);
543   return(dst);
544 }
545
546 unsigned long
547 irc_ns_get32(const unsigned char *src)
548 {
549   unsigned long dst;
550
551   IRC_NS_GET32(dst, src);
552   return(dst);
553 }
554
555 void
556 irc_ns_put16(unsigned int src, unsigned char *dst)
557 {
558   IRC_NS_PUT16(src, dst);
559 }
560
561 void
562 irc_ns_put32(unsigned long src, unsigned char *dst)
563 {
564   IRC_NS_PUT32(src, dst);
565 }
566
567 /* From ns_name.c */
568
569 /*
570  * special(ch)
571  *      Thinking in noninternationalized USASCII (per the DNS spec),
572  *      is this characted special ("in need of quoting") ?
573  * return:
574  *      boolean.
575  */
576 static int
577 special(int ch)
578 {
579   switch (ch)
580   {
581     case 0x22: /* '"'  */
582     case 0x2E: /* '.'  */
583     case 0x3B: /* ';'  */
584     case 0x5C: /* '\\' */
585     case 0x28: /* '('  */
586     case 0x29: /* ')'  */
587     /* Special modifiers in zone files. */
588     case 0x40: /* '@'  */
589     case 0x24: /* '$'  */
590       return(1);
591     default:
592       return(0);
593   }
594 }
595
596 static int
597 labellen(const unsigned char *lp)
598 {                               
599   int bitlen;
600   unsigned char l = *lp;
601
602   if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS)
603   {
604     /* should be avoided by the caller */
605     return(-1);
606   }
607
608   if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT)
609   {
610     if (l == DNS_LABELTYPE_BITSTRING)
611     {
612       if ((bitlen = *(lp + 1)) == 0)
613         bitlen = 256;
614       return((bitlen + 7 ) / 8 + 1);
615     }
616
617     return(-1); /* unknwon ELT */
618   }
619
620   return(l);
621 }
622
623
624 /*
625  * printable(ch)
626  *      Thinking in noninternationalized USASCII (per the DNS spec),
627  *      is this character visible and not a space when printed ?
628  * return:
629  *      boolean.
630  */
631 static int
632 printable(int ch)
633 {
634   return(ch > 0x20 && ch < 0x7f);
635 }
636
637 static int
638 irc_decode_bitstring(const char **cpp, char *dn, const char *eom)
639 {
640         const char *cp = *cpp;
641         char *beg = dn, tc;
642         int b, blen, plen;
643
644         if ((blen = (*cp & 0xff)) == 0)
645                 blen = 256;
646         plen = (blen + 3) / 4;
647         plen += sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1);
648         if (dn + plen >= eom)
649                 return(-1);
650
651         cp++;
652         dn += sprintf(dn, "\\[x");
653         for (b = blen; b > 7; b -= 8, cp++)
654                 dn += sprintf(dn, "%02x", *cp & 0xff);
655         if (b > 4) {
656                 tc = *cp++;
657                 dn += sprintf(dn, "%02x", tc & (0xff << (8 - b)));
658         } else if (b > 0) {
659                 tc = *cp++;
660                dn += sprintf(dn, "%1x",
661                                ((tc >> 4) & 0x0f) & (0x0f << (4 - b)));
662         }
663         dn += sprintf(dn, "/%d]", blen);
664
665         *cpp = cp;
666         return(dn - beg);
667 }
668
669 /*
670  * irc_ns_name_pton(src, dst, dstsiz)
671  *  Convert a ascii string into an encoded domain name as per RFC1035.
672  * return:
673  *  -1 if it fails
674  *  1 if string was fully qualified
675  *  0 is string was not fully qualified
676  * notes:
677  *  Enforces label and domain length limits.
678  */
679 int
680 irc_ns_name_pton(const char *src, unsigned char *dst, size_t dstsiz)
681 {
682   unsigned char *label, *bp, *eom;
683   char *cp;
684   int c, n, escaped, e = 0;
685
686   escaped = 0;
687   bp = dst;
688   eom = dst + dstsiz;
689   label = bp++;
690
691
692   while ((c = *src++) != 0) {
693     if (escaped) {
694       if (c == '[') { /* start a bit string label */
695         if ((cp = strchr(src, ']')) == NULL) {
696           errno = EINVAL; /* ??? */
697           return(-1);
698         }
699         if ((e = irc_encode_bitsring(&src,
700                cp + 2,
701                &label,
702                &bp,
703                (const char *)eom))
704             != 0) {
705           errno = e;
706           return(-1);
707         }
708         escaped = 0;
709         label = bp++;
710         if ((c = *src++) == 0)
711           goto done;
712         else if (c != '.') {
713           errno = EINVAL;
714           return(-1);
715         }
716         continue;
717       }
718       else if ((cp = strchr(digits, c)) != NULL) {
719         n = (cp - digits) * 100;
720         if ((c = *src++) == 0 ||
721             (cp = strchr(digits, c)) == NULL) {
722           errno = EMSGSIZE;
723           return (-1);
724         }
725         n += (cp - digits) * 10;
726         if ((c = *src++) == 0 ||
727             (cp = strchr(digits, c)) == NULL) {
728           errno = EMSGSIZE;
729           return (-1);
730         }
731         n += (cp - digits);
732         if (n > 255) {
733           errno = EMSGSIZE;
734           return (-1);
735         }
736         c = n;
737       }
738       escaped = 0;
739     } else if (c == '\\') {
740       escaped = 1;
741       continue;
742     } else if (c == '.') {
743       c = (bp - label - 1);
744       if ((c & NS_CMPRSFLGS) != 0) {  /* Label too big. */
745         errno = EMSGSIZE;
746         return (-1);
747       }
748       if (label >= eom) {
749         errno = EMSGSIZE;
750         return (-1);
751       }
752       *label = c;
753       /* Fully qualified ? */
754       if (*src == '\0') {
755         if (c != 0) {
756           if (bp >= eom) {
757             errno = EMSGSIZE;
758             return (-1);
759           }
760           *bp++ = '\0';
761         }
762         if ((bp - dst) > NS_MAXCDNAME) {
763           errno = EMSGSIZE;
764           return (-1);
765         }
766         return (1);
767       }
768       if (c == 0 || *src == '.') {
769         errno = EMSGSIZE;
770         return (-1);
771       }
772       label = bp++;
773       continue;
774     }
775     if (bp >= eom) {
776       errno = EMSGSIZE;
777       return (-1);
778     }
779     *bp++ = (unsigned char)c;
780   }
781   c = (bp - label - 1);
782   if ((c & NS_CMPRSFLGS) != 0) {    /* Label too big. */
783     errno = EMSGSIZE;
784     return (-1);
785   }
786   done:
787   if (label >= eom) {
788     errno = EMSGSIZE;
789     return (-1);
790   }
791   *label = c;
792   if (c != 0) {
793     if (bp >= eom) {
794       errno = EMSGSIZE;
795       return (-1);
796     }
797     *bp++ = 0;
798   }
799
800   if ((bp - dst) > NS_MAXCDNAME)
801   { /* src too big */
802     errno = EMSGSIZE;
803     return (-1);
804   }
805
806   return (0);
807 }
808
809 /*
810  * irc_ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr)
811  *  Pack domain name 'domain' into 'comp_dn'.
812  * return:
813  *  Size of the compressed name, or -1.
814  * notes:
815  *  'dnptrs' is an array of pointers to previous compressed names.
816  *  dnptrs[0] is a pointer to the beginning of the message. The array
817  *  ends with NULL.
818  *  'lastdnptr' is a pointer to the end of the array pointed to
819  *  by 'dnptrs'.
820  * Side effects:
821  *  The list of pointers in dnptrs is updated for labels inserted into
822  *  the message as we compress the name.  If 'dnptr' is NULL, we don't
823  *  try to compress names. If 'lastdnptr' is NULL, we don't update the
824  *  list.
825  */
826 int
827 irc_ns_name_pack(const unsigned char *src, unsigned char *dst, int dstsiz,
828                  const unsigned char **dnptrs, const unsigned char **lastdnptr)
829 {
830   unsigned char *dstp;
831   const unsigned char **cpp, **lpp, *eob, *msg;
832   const unsigned char *srcp;
833   int n, l, first = 1;
834
835   srcp = src;
836   dstp = dst;
837   eob = dstp + dstsiz;
838   lpp = cpp = NULL;
839   if (dnptrs != NULL) {
840     if ((msg = *dnptrs++) != NULL) {
841       for (cpp = dnptrs; *cpp != NULL; cpp++)
842         (void)NULL;
843       lpp = cpp;  /* end of list to search */
844     }
845   } else
846     msg = NULL;
847
848   /* make sure the domain we are about to add is legal */
849   l = 0;
850   do {
851     int l0;
852
853     n = *srcp;
854     if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
855       errno = EMSGSIZE;
856       return (-1);
857     }
858     if ((l0 = labellen(srcp)) < 0) {
859       errno = EINVAL;
860       return(-1);
861     }
862     l += l0 + 1;
863     if (l > NS_MAXCDNAME) {
864       errno = EMSGSIZE;
865       return (-1);
866     }
867     srcp += l0 + 1;
868   } while (n != 0);
869
870   /* from here on we need to reset compression pointer array on error */
871   srcp = src;
872   do {
873     /* Look to see if we can use pointers. */
874     n = *srcp;
875     if (n != 0 && msg != NULL) {
876       l = irc_dn_find(srcp, msg, (const unsigned char * const *)dnptrs,
877             (const unsigned char * const *)lpp);
878       if (l >= 0) {
879         if (dstp + 1 >= eob) {
880           goto cleanup;
881         }
882         *dstp++ = (l >> 8) | NS_CMPRSFLGS;
883         *dstp++ = l % 256;
884         return (dstp - dst);
885       }
886       /* Not found, save it. */
887       if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
888           (dstp - msg) < 0x4000 && first) {
889         *cpp++ = dstp;
890         *cpp = NULL;
891         first = 0;
892       }
893     }
894     /* copy label to buffer */
895     if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
896       /* Should not happen. */
897       goto cleanup;
898     }
899     n = labellen(srcp);
900     if (dstp + 1 + n >= eob) {
901       goto cleanup;
902     }
903     memcpy(dstp, srcp, n + 1);
904     srcp += n + 1;
905     dstp += n + 1;
906   } while (n != 0);
907
908   if (dstp > eob) {
909 cleanup:
910     if (msg != NULL)
911       *lpp = NULL;
912     errno = EMSGSIZE;
913     return (-1);
914   }
915   return(dstp - dst);
916 }
917
918 static int
919 irc_ns_name_compress(const char *src, unsigned char *dst, size_t dstsiz,
920                      const unsigned char **dnptrs, const unsigned char **lastdnptr)
921 {
922   unsigned char tmp[NS_MAXCDNAME];
923
924   if (irc_ns_name_pton(src, tmp, sizeof tmp) == -1)
925     return(-1);
926   return(irc_ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
927 }
928
929 static int
930 irc_encode_bitsring(const char **bp, const char *end, unsigned char **labelp,
931                     unsigned char **dst, const char *eom)
932 {
933   int afterslash = 0;
934   const char *cp = *bp;
935   char *tp, c;
936   const char *beg_blen;
937   char *end_blen = NULL;
938   int value = 0, count = 0, tbcount = 0, blen = 0;
939
940   beg_blen = end_blen = NULL;
941
942   /* a bitstring must contain at least 2 characters */
943   if (end - cp < 2)
944     return(EINVAL);
945
946   /* XXX: currently, only hex strings are supported */
947   if (*cp++ != 'x')
948     return(EINVAL);
949   if (!isxdigit((*cp) & 0xff)) /* reject '\[x/BLEN]' */
950     return(EINVAL);
951
952   for (tp = (char*)(*dst + 1); cp < end && tp < eom; cp++) {
953     switch((c = *cp)) {
954     case ']': /* end of the bitstring */
955       if (afterslash) {
956         if (beg_blen == NULL)
957           return(EINVAL);
958         blen = (int)strtol(beg_blen, &end_blen, 10);
959         if (*end_blen != ']')
960           return(EINVAL);
961       }
962       if (count)
963         *tp++ = ((value << 4) & 0xff);
964       cp++; /* skip ']' */
965       goto done;
966     case '/':
967       afterslash = 1;
968       break;
969     default:
970       if (afterslash) {
971         if (!isdigit(c&0xff))
972           return(EINVAL);
973         if (beg_blen == NULL) {
974
975           if (c == '0') {
976             /* blen never begings with 0 */
977             return(EINVAL);
978           }
979           beg_blen = cp;
980         }
981       } else {
982         if (!isxdigit(c&0xff))
983           return(EINVAL);
984         value <<= 4;
985         value += digitvalue[(int)c];
986         count += 4;
987         tbcount += 4;
988         if (tbcount > 256)
989           return(EINVAL);
990         if (count == 8) {
991           *tp++ = value;
992           count = 0;
993         }
994       }
995       break;
996     }
997   }
998   done:
999   if (cp >= end || tp >= eom)
1000     return(EMSGSIZE);
1001
1002   /*
1003    * bit length validation:
1004    * If a <length> is present, the number of digits in the <bit-data>
1005    * MUST be just sufficient to contain the number of bits specified
1006    * by the <length>. If there are insignificant bits in a final
1007    * hexadecimal or octal digit, they MUST be zero.
1008    * RFC 2673, Section 3.2.
1009    */
1010   if (blen > 0) {
1011     int traillen;
1012
1013     if (((blen + 3) & ~3) != tbcount)
1014       return(EINVAL);
1015     traillen = tbcount - blen; /* between 0 and 3 */
1016     if (((value << (8 - traillen)) & 0xff) != 0)
1017       return(EINVAL);
1018   }
1019   else
1020     blen = tbcount;
1021   if (blen == 256)
1022     blen = 0;
1023
1024   /* encode the type and the significant bit fields */
1025   **labelp = DNS_LABELTYPE_BITSTRING;
1026   **dst = blen;
1027
1028   *bp = cp;
1029   *dst = (unsigned char*)tp;
1030
1031   return(0);
1032 }
1033
1034 /*
1035  * dn_find(domain, msg, dnptrs, lastdnptr)
1036  *  Search for the counted-label name in an array of compressed names.
1037  * return:
1038  *  offset from msg if found, or -1.
1039  * notes:
1040  *  dnptrs is the pointer to the first name on the list,
1041  *  not the pointer to the start of the message.
1042  */
1043 static int
1044 irc_dn_find(const unsigned char *domain, const unsigned char *msg,
1045             const unsigned char * const *dnptrs,
1046             const unsigned char * const *lastdnptr)
1047 {
1048   const unsigned char *dn, *cp, *sp;
1049   const unsigned char * const *cpp;
1050   unsigned int n;
1051
1052   for (cpp = dnptrs; cpp < lastdnptr; cpp++)
1053   {
1054     sp = *cpp;
1055     /*
1056      * terminate search on:
1057      * root label
1058      * compression pointer
1059      * unusable offset
1060      */
1061     while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 &&
1062            (sp - msg) < 0x4000) {
1063       dn = domain;
1064       cp = sp;
1065       while ((n = *cp++) != 0) {
1066         /*
1067          * check for indirection
1068          */
1069         switch (n & NS_CMPRSFLGS) {
1070         case 0:   /* normal case, n == len */
1071           n = labellen(cp - 1); /* XXX */
1072
1073           if (n != *dn++)
1074             goto next;
1075
1076           for ((void)NULL; n > 0; n--)
1077             if (mklower(*dn++) !=
1078                 mklower(*cp++))
1079               goto next;
1080           /* Is next root for both ? */
1081           if (*dn == '\0' && *cp == '\0')
1082             return (sp - msg);
1083           if (*dn)
1084             continue;
1085           goto next;
1086         case NS_CMPRSFLGS:  /* indirection */
1087           cp = msg + (((n & 0x3f) << 8) | *cp);
1088           break;
1089
1090         default:  /* illegal type */
1091           errno = EMSGSIZE;
1092           return (-1);
1093         }
1094       }
1095  next: ;
1096       sp += *sp + 1;
1097     }
1098   }
1099   errno = ENOENT;
1100   return (-1);
1101 }
1102
1103 /*
1104  * Thinking in noninternationalized USASCII (per the DNS spec),
1105  * convert this character to lower case if it's upper case.
1106  */
1107 static int
1108 mklower(int ch) 
1109 {
1110   if (ch >= 0x41 && ch <= 0x5A)
1111     return(ch + 0x20);
1112
1113   return(ch);
1114 }
1115
1116 /* From resolv/mkquery.c */
1117
1118 /*
1119  * Form all types of queries.
1120  * Returns the size of the result or -1.
1121  */
1122 int
1123 irc_res_mkquery(
1124              const char *dname,         /* domain name */
1125              int class, int type,       /* class and type of query */
1126              unsigned char *buf,                /* buffer to put query */
1127              int buflen)                /* size of buffer */
1128 {
1129         HEADER *hp;
1130         unsigned char *cp;
1131         int n;
1132         unsigned char *dnptrs[20], **dpp, **lastdnptr;
1133
1134         /*
1135          * Initialize header fields.
1136          */
1137         if ((buf == NULL) || (buflen < HFIXEDSZ))
1138                 return (-1);
1139         memset(buf, 0, HFIXEDSZ);
1140         hp = (HEADER *) buf;
1141
1142         hp->id = 0;
1143         hp->opcode = QUERY;
1144         hp->rd = 1;             /* recurse */
1145         hp->rcode = NO_ERRORS;
1146         cp = buf + HFIXEDSZ;
1147         buflen -= HFIXEDSZ;
1148         dpp = dnptrs;
1149         *dpp++ = buf;
1150         *dpp++ = NULL;
1151         lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];
1152
1153         if ((buflen -= QFIXEDSZ) < 0)
1154           return (-1);
1155         if ((n = irc_dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0)
1156           return (-1);
1157
1158         cp += n;
1159         buflen -= n;
1160         IRC_NS_PUT16(type, cp);
1161         IRC_NS_PUT16(class, cp);
1162         hp->qdcount = htons(1);
1163
1164         return (cp - buf);
1165 }