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