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