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