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