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