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