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