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