From: Apple Date: Thu, 11 Oct 2007 00:27:09 +0000 (+0000) Subject: libresolv-19.tar.gz X-Git-Tag: v19 X-Git-Url: https://git.saurik.com/apple/libresolv.git/commitdiff_plain/bfd21550e734c2ce58048e6d572e12ba2ceacdc6 libresolv-19.tar.gz --- 8a97ab44e35a0ae5457203472875cb92bc11e419 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9f28573 --- /dev/null +++ b/Makefile @@ -0,0 +1,64 @@ +# +# Generated by the Apple Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = resolv + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Library + +HFILES = dns.h dns_private.h dns_util.h dst.h dst_internal.h\ + nameser.h res_debug.h res_private.h res_update.h resolv.h + +CFILES = base64.c dns.c dns_async.c dns_util.c dst_api.c\ + dst_hmac_link.c dst_support.c ns_date.c ns_name.c ns_netint.c\ + ns_parse.c ns_print.c ns_samedomain.c ns_sign.c ns_ttl.c\ + ns_verify.c res_comp.c res_data.c res_debug.c\ + res_findzonecut.c res_init.c res_mkquery.c res_mkupdate.c\ + res_query.c res_send.c res_sendsigned.c res_update.c + +OTHERSRCS = Makefile Makefile.postamble Makefile.preamble resolver.3 resolver_so.3 resolver.5 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CURRENTLY_ACTIVE_VERSION = YES +DEPLOY_WITH_VERSION_NAME = 9 +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = library.make +NEXTSTEP_INSTALLDIR = /usr/lib +LIBS = -ldnsinfo +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + +PUBLIC_HEADERS = dns.h dns_util.h nameser.h resolv.h + +PROJECT_HEADERS = dns.h dns_private.h dns_util.h dst.h dst_internal.h\ + nameser.h res_debug.h res_private.h res_update.h\ + resolv.h + + + +WINDOWS_PUBLIC_HEADERS_DIR = LOCAL_DEVELOPER_DIR/Headers/$(NAME) + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(JDKBINDIR)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/Makefile.postamble b/Makefile.postamble new file mode 100644 index 0000000..5a72628 --- /dev/null +++ b/Makefile.postamble @@ -0,0 +1,31 @@ +arpa_headers: $(DSTROOT)$(PUBLIC_HDR_INSTALLDIR)$(ARPA_HEADER_DIR_SUFFIX) + $(RM) -f $(foreach header, $(ARPA_SYMLINKED_HEADERS), $(DSTROOT)$(PUBLIC_HDR_INSTALLDIR)$(ARPA_HEADER_DIR_SUFFIX)/$(header)) + $(LN) -s $(foreach header, $(ARPA_SYMLINKED_HEADERS), ../$(header)) $(DSTROOT)$(PUBLIC_HDR_INSTALLDIR)$(ARPA_HEADER_DIR_SUFFIX) + $(SILENT) $(FASTCP) $(ARPA_HEADERS) $(DSTROOT)$(PUBLIC_HDR_INSTALLDIR)$(ARPA_HEADER_DIR_SUFFIX) + +$(DSTROOT)$(PUBLIC_HDR_INSTALLDIR)$(ARPA_HEADER_DIR_SUFFIX): + $(MKDIRS) $@ + +after_install: $(DSTROOT)/usr/share/man/man3 $(DSTROOT)/usr/share/man/man5 + install -d $(DSTROOT)/usr/share/man/man3 + install -c -m 644 resolver.3 $(DSTROOT)/usr/share/man/man3 + install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/dn_comp.3 + install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/dn_expand.3 + install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/dn_skipname.3 + install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/ns_get16.3 + install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/ns_get32.3 + install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/ns_put16.3 + install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/ns_put32.3 + install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/res_init.3 + install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/res_mkquery.3 + install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/res_query.3 + install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/res_search.3 + install -c -m 644 resolver_so.3 $(DSTROOT)/usr/share/man/man3/res_send.3 + install -d $(DSTROOT)/usr/share/man/man5 + install -c -m 444 resolver.5 $(DSTROOT)/usr/share/man/man5 + +$(DSTROOT)/usr/share/man/man3: + $(MKDIRS) $@ + +$(DSTROOT)/usr/share/man/man5: + $(MKDIRS) $@ diff --git a/Makefile.preamble b/Makefile.preamble new file mode 100644 index 0000000..e50fc53 --- /dev/null +++ b/Makefile.preamble @@ -0,0 +1,8 @@ +PUBLIC_HEADER_DIR=/usr/include +PUBLIC_HEADER_DIR_SUFFIX = +ARPA_HEADER_DIR_SUFFIX = /arpa +ARPA_SYMLINKED_HEADERS = nameser.h +AFTER_INSTALLHDRS += arpa_headers + +PRIVATE_HEADER_DIR=/usr/local/include +OTHER_PRIVATE_HEADERS += dns_private.h diff --git a/PB.project b/PB.project new file mode 100644 index 0000000..0862d98 --- /dev/null +++ b/PB.project @@ -0,0 +1,82 @@ +{ + "CURRENTLY_ACTIVE_VERSION" = YES; + "DEPLOY_WITH_VERSION_NAME" = 9; + "DYNAMIC_CODE_GEN" = YES; + FILESTABLE = { + CLASSES = (); + HEADERSEARCH = (); + "H_FILES" = ( + "dns.h", + "dns_private.h", + "dns_util.h", + "dst.h", + "dst_internal.h", + "nameser.h", + "res_debug.h", + "res_private.h", + "res_update.h", + "resolv.h" + ); + "OTHER_LINKED" = ( + "base64.c", + "dns.c", + "dns_async.c", + "dns_util.c", + "dst_api.c", + "dst_hmac_link.c", + "dst_support.c", + "ns_date.c", + "ns_name.c", + "ns_netint.c", + "ns_parse.c", + "ns_print.c", + "ns_samedomain.c", + "ns_sign.c", + "ns_ttl.c", + "ns_verify.c", + "res_comp.c", + "res_data.c", + "res_debug.c", + "res_findzonecut.c", + "res_init.c", + "res_mkquery.c", + "res_mkupdate.c", + "res_query.c", + "res_send.c", + "res_sendsigned.c", + "res_update.c" + ); + "OTHER_SOURCES" = (Makefile, "Makefile.postamble", "Makefile.preamble", "resolver.5"); + "PROJECT_HEADERS" = ( + "dns.h", + "dns_private.h", + "dns_util.h", + "dst.h", + "dst_internal.h", + "nameser.h", + "res_debug.h", + "res_private.h", + "res_update.h", + "resolv.h" + ); + "PUBLIC_HEADERS" = ("dns.h", "dns_util.h", "nameser.h", "resolv.h"); + SUBPROJECTS = (); + }; + LANGUAGE = English; + "LOCALIZABLE_FILES" = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + "NEXTSTEP_BUILDTOOL" = "/usr/bin/gnumake"; + "NEXTSTEP_INSTALLDIR" = "/usr/lib"; + "NEXTSTEP_JAVA_COMPILER" = "/usr/bin/javac"; + "NEXTSTEP_OBJCPLUS_COMPILER" = "/usr/bin/cc"; + "PDO_UNIX_BUILDTOOL" = "$NEXT_ROOT/Developer/bin/make"; + "PDO_UNIX_JAVA_COMPILER" = "$(JDKBINDIR)/javac"; + "PDO_UNIX_OBJCPLUS_COMPILER" = "$(NEXTDEV_BIN)/gcc"; + PROJECTNAME = resolv; + PROJECTTYPE = Library; + PROJECTVERSION = "2.8"; + "WINDOWS_BUILDTOOL" = "$NEXT_ROOT/Developer/Executables/make"; + "WINDOWS_JAVA_COMPILER" = "$(JDKBINDIR)/javac.exe"; + "WINDOWS_OBJCPLUS_COMPILER" = "$(DEVDIR)/gcc"; + "WINDOWS_PUBLICHEADERSDIR" = "LOCAL_DEVELOPER_DIR/Headers/$(NAME)"; +} diff --git a/base64.c b/base64.c new file mode 100644 index 0000000..d8826e7 --- /dev/null +++ b/base64.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __APPLE__ +#if !defined(LINT) && !defined(CODECENTER) +static const char rcsid[] = "$Id: base64.c,v 1.1 2006/03/01 19:01:34 majka Exp $"; +#endif /* not lint */ +#endif + +#ifndef __APPLE__ +#include "port_before.h" +#endif + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef __APPLE__ +#include "port_after.h" +#endif + +#define Assert(Cond) if (!(Cond)) abort() + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int +b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) { + size_t datalength = 0; + u_char input[3]; + u_char output[4]; + size_t i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + Assert(output[3] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +b64_pton(src, target, targsize) + char const *src; + u_char *target; + size_t targsize; +{ + int tarindex, state, ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + abort(); + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} diff --git a/dns.c b/dns.c new file mode 100644 index 0000000..b121691 --- /dev/null +++ b/dns.c @@ -0,0 +1,1606 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "dns.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dns_private.h" +#include "res_private.h" + +#define INET_NTOP_AF_INET_OFFSET 4 +#define INET_NTOP_AF_INET6_OFFSET 8 + +#define SEARCH_COUNT_INIT -1 + +#define DNS_RESOLVER_DIR "/etc/resolver" + +#define NOTIFY_DIR_NAME "com.apple.system.dns.resolver.dir" +#define DNS_DELAY_NAME "com.apple.system.dns.delay" + +#define DNS_DELAY_INTERVAL 4 + +#define DNS_PRIVATE_HANDLE_TYPE_SUPER 0 +#define DNS_PRIVATE_HANDLE_TYPE_PLAIN 1 + +#define MDNS_MIN_TTL 2 + +extern uint32_t notify_monitor_file(int token, const char *name, int flags); + +extern void res_client_close(res_state res); +extern res_state res_state_new(); +extern int res_nquery_soa_min(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, int *fromlen, int *min); +extern int res_nsearch_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen); +extern int __res_nsearch_list_2(res_state statp, const char *name, int class, int type, u_char *answer, int anslen, struct sockaddr *from, uint32_t *fromlen, int nsearch, char **search); + +extern char *res_next_word(char **p); +extern res_state res_build_start(res_state res); +extern int res_build(res_state res, uint16_t port, uint32_t *nsrch, char *key, char *val); +extern int res_build_sortlist(res_state res, struct in_addr addr, struct in_addr mask); + +static void +_pdns_set_name(pdns_handle_t *pdns, const char *name) +{ + int n; + + if (pdns == NULL) return; + if (name == NULL) return; + + /* only set the name once */ + if (pdns->name != NULL) return; + + /* strip trailing dots */ + n = strlen(name) - 1; + while ((n >= 0) && (name[n] == '.')) n--; + + if (n < 0) return; + + n++; + pdns->name = calloc(n + 1, sizeof(char)); + if (pdns->name == NULL) return; + memcpy(pdns->name, name, n); +} + +static pdns_handle_t * +_pdns_build_start(char *name) +{ + pdns_handle_t *pdns; + + pdns = (pdns_handle_t *)calloc(1, sizeof(pdns_handle_t)); + if (pdns == NULL) return NULL; + + pdns->res = res_build_start(NULL); + if (pdns->res == NULL) + { + free(pdns); + return NULL; + } + + _pdns_set_name(pdns, name); + pdns->port = NS_DEFAULTPORT; + + return pdns; +} + +static int +_pdns_build_finish(pdns_handle_t *pdns) +{ + uint32_t n; + + if (pdns == NULL) return -1; + + n = pdns->res->nscount; + if (n == 0) n = 1; + + if (pdns->total_timeout == 0) + { + if (pdns->send_timeout == 0) pdns->total_timeout = RES_MAXRETRANS; + else pdns->total_timeout = pdns->send_timeout * pdns->res->retry * n; + } + + if (pdns->total_timeout == 0) pdns->res->retrans = RES_MAXRETRANS; + else pdns->res->retrans = pdns->total_timeout; + + pdns->res->options |= RES_INIT; + + return 0; +} + +static int +_pdns_build_sortlist(pdns_handle_t *pdns, struct in_addr addr, struct in_addr mask) +{ + if (pdns == NULL) return -1; + return res_build_sortlist(pdns->res, addr, mask); +} + +static void +_pdns_free(pdns_handle_t *pdns) +{ + int i; + + if (pdns == NULL) return; + + if ((pdns->search_count != SEARCH_COUNT_INIT) && (pdns->search_count > 0) && (pdns->search_list != NULL)) + { + for (i = 0; i < pdns->search_count; i++) free(pdns->search_list[i]); + free(pdns->search_list); + } + + if (pdns->name != NULL) free(pdns->name); + if (pdns->res != NULL) res_client_close(pdns->res); + + free(pdns); +} + +static int +_pdns_build(pdns_handle_t *pdns, char *key, char *val) +{ + struct in6_addr addr6; + int32_t status; + char *dupval; + + if (pdns == NULL) return -1; + + /* + * Detect IPv6 server addresses. + */ + if (((pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) == 0) && (!strcmp(key, "nameserver"))) + { + memset(&addr6, 0, sizeof(struct in6_addr)); + status = inet_pton(AF_INET6, val, &addr6); + if (status == 1) pdns->flags |= DNS_FLAG_HAVE_IPV6_SERVER; + } + + /* + * We handle some keys here. + * Other keys get passed on to res_build. + */ + if (!strcmp(key, "default")) + { + pdns->flags |= DNS_FLAG_DEFAULT_RESOLVER; + return 0; + } + + if (!strcmp(key, "port")) + { + pdns->port = atoi(val); + return 0; + } + + if (!strcmp(key, "search")) + { + dupval = strdup(val); + if (dupval == NULL) return -1; + + if (pdns->search_count == SEARCH_COUNT_INIT) pdns->search_count = 0; + if (pdns->search_count == 0) + { + pdns->search_list = (char **)calloc(1, sizeof(char *)); + } + else + { + pdns->search_list = (char **)reallocf(pdns->search_list, (pdns->search_count + 1) * sizeof(char *)); + } + + if (pdns->search_list == NULL) + { + free(dupval); + _pdns_free(pdns); + return -1; + } + + pdns->search_list[pdns->search_count] = dupval; + pdns->search_count++; + return 0; + } + + if (!strcmp(key, "total_timeout")) + { + pdns->total_timeout = atoi(val); + return 0; + } + + if (!strcmp(key, "timeout")) + { + pdns->send_timeout = atoi(val); + return 0; + } + + if (!strcmp(key, "search_order")) + { + pdns->search_order = atoi(val); + return 0; + } + + if (!strcmp(key, "pdns")) + { + pdns->flags |= DNS_FLAG_FORWARD_TO_MDNSRESPONDER; + return 0; + } + + if (!strcmp(key, "mdns")) + { + pdns->flags |= DNS_FLAG_FORWARD_TO_MDNSRESPONDER; + return 0; + } + + /* pass on to res_build */ + return res_build(pdns->res, pdns->port, &(pdns->search_count), key, val); +} + +static pdns_handle_t * +_pdns_convert_sc(dns_resolver_t *r) +{ + pdns_handle_t *pdns; + char *val, *p, *x; + int i; + + pdns = _pdns_build_start(r->domain); + if (r->domain == NULL) _pdns_build(pdns, "default", NULL); + + p = getenv("RES_RETRY_TIMEOUT"); + if (p != NULL) pdns->send_timeout = atoi(p); + + p = getenv("RES_RETRY"); + if (p != NULL) pdns->res->retry= atoi(p); + + if (r->port != 0) + { + val = NULL; + asprintf(&val, "%hu", r->port); + if (val == NULL) + { + _pdns_free(pdns); + return NULL; + } + + _pdns_build(pdns, "port", val); + free(val); + } + + if (r->n_nameserver > MAXNS) r->n_nameserver = MAXNS; + for (i = 0; i < r->n_nameserver; i++) + { + if (r->nameserver[i]->sa_family == AF_INET) + { + val = calloc(1, 256); + if (val == NULL) + { + _pdns_free(pdns); + return NULL; + } + + inet_ntop(AF_INET, (char *)(r->nameserver[i]) + INET_NTOP_AF_INET_OFFSET, val, 256); + _pdns_build(pdns, "nameserver", val); + free(val); + } + else if (r->nameserver[i]->sa_family == AF_INET6) + { + pdns->flags |= DNS_FLAG_HAVE_IPV6_SERVER; + val = calloc(1, 256); + if (val == NULL) + { + _pdns_free(pdns); + return NULL; + } + + inet_ntop(AF_INET6, (char *)(r->nameserver[i]) + INET_NTOP_AF_INET6_OFFSET, val, 256); + _pdns_build(pdns, "nameserver", val); + free(val); + } + } + + if (r->n_search > MAXDNSRCH) r->n_search = MAXDNSRCH; + for (i = 0; i < r->n_search; i++) + { + val = NULL; + asprintf(&val, "%s", r->search[i]); + if (val == NULL) + { + _pdns_free(pdns); + return NULL; + } + + _pdns_build(pdns, "search", val); + free(val); + } + + if (r->timeout > 0) + { + val = NULL; + asprintf(&val, "%d", r->timeout); + if (val == NULL) + { + _pdns_free(pdns); + return NULL; + } + + _pdns_build(pdns, "total_timeout", val); + free(val); + } + + val = NULL; + asprintf(&val, "%d", r->search_order); + if (val == NULL) + { + _pdns_free(pdns); + return NULL; + } + + _pdns_build(pdns, "search_order", val); + free(val); + + if (r->n_sortaddr > MAXRESOLVSORT) r->n_sortaddr = MAXRESOLVSORT; + for (i = 0; i < r->n_sortaddr; i++) + { + _pdns_build_sortlist(pdns, r->sortaddr[i]->address, r->sortaddr[i]->mask); + } + + p = r->options; + while (NULL != (x = res_next_word(&p))) + { + /* search for and process individual options */ + if (!strncmp(x, "ndots:", 6)) + { + _pdns_build(pdns, "ndots", x+6); + } + + else if (!strncmp(x, "nibble:", 7)) + { + _pdns_build(pdns, "nibble", x+7); + } + + else if (!strncmp(x, "nibble2:", 8)) + { + _pdns_build(pdns, "nibble2", x+8); + } + + else if (!strncmp(x, "timeout:", 8)) + { + _pdns_build(pdns, "timeout", x+8); + } + + else if (!strncmp(x, "attempts:", 9)) + { + _pdns_build(pdns, "attempts", x+9); + } + + else if (!strncmp(x, "bitstring:", 10)) + { + _pdns_build(pdns, "bitstring", x+10); + } + + else if (!strncmp(x, "v6revmode:", 10)) + { + _pdns_build(pdns, "v6revmode", x+10); + } + + else if (!strcmp(x, "debug")) + { + _pdns_build(pdns, "debug", NULL); + } + + else if (!strcmp(x, "no_tld_query")) + { + _pdns_build(pdns, "no_tld_query", NULL); + } + + else if (!strcmp(x, "inet6")) + { + _pdns_build(pdns, "inet6", NULL); + } + + else if (!strcmp(x, "rotate")) + { + _pdns_build(pdns, "rotate", NULL); + } + + else if (!strcmp(x, "no-check-names")) + { + _pdns_build(pdns, "no-check-names", NULL); + } + +#ifdef RES_USE_EDNS0 + else if (!strcmp(x, "edns0")) + { + _pdns_build(pdns, "edns0", NULL); + } +#endif + else if (!strcmp(x, "a6")) + { + _pdns_build(pdns, "a6", NULL); + } + + else if (!strcmp(x, "dname")) + { + _pdns_build(pdns, "dname", NULL); + } + + else if (!strcmp(x, "default")) + { + _pdns_build(pdns, "default", NULL); + } + + else if (!strcmp(x, "pdns")) + { + _pdns_build(pdns, "pdns", NULL); + } + + else if (!strcmp(x, "mdns")) + { + _pdns_build(pdns, "mdns", NULL); + } + } + + _pdns_build_finish(pdns); + return pdns; +} + +/* + * Open a named resolver client from the system config data. + */ +static pdns_handle_t * +_pdns_sc_open(const char *name) +{ + pdns_handle_t *pdns; + int i; + dns_config_t *sc_dns; + dns_resolver_t *sc_res; + + sc_dns = dns_configuration_copy(); + if (sc_dns == NULL) return NULL; + + sc_res = NULL; + + if (name == NULL) + { + if (sc_dns->n_resolver != 0) sc_res = sc_dns->resolver[0]; + } + else + { + for (i = 0; (sc_res == NULL) && (i < sc_dns->n_resolver); i++) + { + if (sc_dns->resolver[i] == NULL) continue; + if (sc_dns->resolver[i]->domain == NULL) continue; + if (!strcasecmp(name, sc_dns->resolver[i]->domain)) sc_res = sc_dns->resolver[i]; + } + } + + if (sc_res == NULL) + { + dns_configuration_free(sc_dns); + return NULL; + } + + pdns = (pdns_handle_t *)calloc(1, sizeof(pdns_handle_t)); + if (pdns == NULL) + { + dns_configuration_free(sc_dns); + return NULL; + } + + pdns = _pdns_convert_sc(sc_res); + + dns_configuration_free(sc_dns); + + if (pdns == NULL) return NULL; + + if (pdns->res == NULL) + { + free(pdns); + return NULL; + } + + pdns->name = NULL; + if (pdns->res->defdname[0] != '\0') _pdns_set_name(pdns, pdns->res->defdname); + else if (name != NULL) _pdns_set_name(pdns, name); + + if (name != NULL) pdns->search_count = SEARCH_COUNT_INIT; + + return pdns; +} + +/* + * Open a named resolver client from file. + */ +static pdns_handle_t * +_pdns_file_open(const char *name) +{ + pdns_handle_t *pdns; + char *path, buf[1024]; + char *p, *x, *y; + FILE *fp; + + path = NULL; + if (name == NULL) + { + asprintf(&path, "%s", _PATH_RESCONF); + } + else if ((name[0] == '.') || (name[0] == '/')) + { + asprintf(&path, "%s", name); + } + else + { + asprintf(&path, "%s/%s", DNS_RESOLVER_DIR, name); + } + + if (path == NULL) return NULL; + + fp = fopen(path, "r"); + free(path); + if (fp == NULL) return NULL; + + pdns = _pdns_build_start(NULL); + if (pdns == NULL) + { + fclose(fp); + return NULL; + } + + p = getenv("RES_RETRY_TIMEOUT"); + if (p != NULL) pdns->send_timeout = atoi(p); + + p = getenv("RES_RETRY"); + if (p != NULL) pdns->res->retry= atoi(p); + + while (fgets(buf, sizeof(buf), fp) != NULL) + { + /* skip comments */ + if ((buf[0] == ';') || (buf[0] == '#')) continue; + p = buf; + x = res_next_word(&p); + if (x == NULL) continue; + if (!strcmp(x, "sortlist")) + { + while (NULL != (x = res_next_word(&p))) + { + _pdns_build(pdns, "sortlist", x); + } + } + else if (!strcmp(x, "timeout")) + { + x = res_next_word(&p); + if (x != NULL) _pdns_build(pdns, "total_timeout", x); + } + else if (!strcmp(x, "options")) + { + while (NULL != (x = res_next_word(&p))) + { + y = strchr(x, ':'); + if (y != NULL) + { + *y = '\0'; + y++; + } + _pdns_build(pdns, x, y); + } + } + else + { + y = res_next_word(&p); + _pdns_build(pdns, x, y); + + if ((!strcmp(x, "domain")) && (pdns->name == NULL)) _pdns_set_name(pdns, y); + } + } + + fclose(fp); + + if (pdns->name == NULL) _pdns_set_name(pdns, name); + + _pdns_build_finish(pdns); + + return pdns; +} + +/* + * If there was no search list, use domain name and parent domain components. + * + * N.B. This code deals with multiple trailing dots, but does not deal with + * multiple internal dots, e.g. "foo.....com". + */ +static void +_pdns_check_search_list(pdns_handle_t *pdns) +{ + int n; + char *p; + + if (pdns == NULL) return; + if (pdns->name == NULL) return; + if (pdns->search_count > 0) return; + + /* Count dots */ + n = 0; + for (p = pdns->name; *p != '\0'; p++) + { + if (*p == '.') n++; + } + + /* Back up over any trailing dots and cut them out of the name */ + for (p--; (p >= pdns->name) && (*p == '.'); p--) + { + *p = '\0'; + n--; + } + + /* This will be true if name was all dots */ + if (p < pdns->name) return; + + /* dots are separators, so number of components is one larger */ + n++; + + _pdns_build(pdns, "search", pdns->name); + + /* Include parent domains with at least LOCALDOMAINPARTS components */ + p = pdns->name; + while (n > LOCALDOMAINPARTS) + { + /* Find next component */ + while ((*p != '.') && (*p != '\0')) p++; + if (*p == '\0') break; + p++; + + n--; + _pdns_build(pdns, "search", p); + } +} + +__private_extern__ void +_check_cache(sdns_handle_t *sdns) +{ + int i, n, status, refresh, sc_dns_count; + DIR *dp; + struct direct *d; + pdns_handle_t *c; + dns_config_t *sc_dns; + + if (sdns == NULL) return; + + refresh = 0; + + if (sdns->stattime == 0) refresh = 1; + + if (refresh == 0) + { + if (sdns->notify_sys_config_token == -1) refresh = 1; + else + { + n = 1; + status = notify_check(sdns->notify_sys_config_token, &n); + if ((status != NOTIFY_STATUS_OK) || (n == 1)) refresh = 1; + } + } + + if (refresh == 0) + { + if (sdns->notify_dir_token == -1) refresh = 1; + else + { + n = 1; + status = notify_check(sdns->notify_dir_token, &n); + if ((status != NOTIFY_STATUS_OK) || (n == 1)) refresh = 1; + } + } + + if (refresh == 0) return; + + /* Free old clients */ + sdns->pdns_primary = NULL; + + for (i = 0; i < sdns->client_count; i++) + { + _pdns_free(sdns->client[i]); + } + + sdns->client_count = 0; + if (sdns->client != NULL) free(sdns->client); + sdns->client = NULL; + + /* Fetch clients from System Configuration */ + sc_dns = dns_configuration_copy(); + + /* Set up Primary resolver. It's the one we consult for a search list */ + sc_dns_count = 0; + if ((sc_dns != NULL) && (sc_dns->n_resolver > 0)) + { + sc_dns_count = sc_dns->n_resolver; + sdns->pdns_primary = _pdns_convert_sc(sc_dns->resolver[0]); + _pdns_check_search_list(sdns->pdns_primary); + } + else + { + sdns->pdns_primary = _pdns_file_open(_PATH_RESCONF); + } + + if (sdns->pdns_primary != NULL) + { + if ((sdns->flags & DNS_FLAG_DEBUG) && (sdns->pdns_primary->res != NULL)) sdns->pdns_primary->res->options |= RES_DEBUG; + if (sdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) sdns->pdns_primary->flags |= DNS_FLAG_OK_TO_SKIP_AAAA; + sdns->pdns_primary->flags |= DNS_FLAG_DEFAULT_RESOLVER; + + sdns->client = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *)); + if (sdns->client == NULL) + { + if (sc_dns != NULL) dns_configuration_free(sc_dns); + return; + } + + sdns->client[sdns->client_count] = sdns->pdns_primary; + sdns->client_count++; + } + + /* Convert System Configuration resolvers */ + for (i = 1; i < sc_dns_count; i++) + { + c = _pdns_convert_sc(sc_dns->resolver[i]); + if (c == NULL) continue; + + if (sdns->flags & DNS_FLAG_DEBUG) c->res->options |= RES_DEBUG; + if (sdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) c->flags |= DNS_FLAG_OK_TO_SKIP_AAAA; + + if (sdns->client_count == 0) + { + sdns->client = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *)); + } + else + { + sdns->client = (pdns_handle_t **)reallocf(sdns->client, (sdns->client_count + 1) * sizeof(pdns_handle_t *)); + } + + if (sdns->client == NULL) + { + sdns->client_count = 0; + dns_configuration_free(sc_dns); + return; + } + + sdns->client[sdns->client_count] = c; + sdns->client_count++; + } + + if (sc_dns != NULL) dns_configuration_free(sc_dns); + + if (sdns->flags & DNS_FLAG_CHECK_RESOLVER_DIR) + { + /* Read /etc/resolvers clients */ + dp = opendir(DNS_RESOLVER_DIR); + if (dp == NULL) + { + sdns->flags &= ~DNS_FLAG_CHECK_RESOLVER_DIR; + } + else + { + while (NULL != (d = readdir(dp))) + { + if (d->d_name[0] == '.') continue; + + c = _pdns_file_open(d->d_name); + if (c == NULL) continue; + if (sdns->flags & DNS_FLAG_DEBUG) c->res->options |= RES_DEBUG; + if (sdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) c->flags |= DNS_FLAG_OK_TO_SKIP_AAAA; + + if (sdns->client_count == 0) + { + sdns->client = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *)); + } + else + { + sdns->client = (pdns_handle_t **)reallocf(sdns->client, (sdns->client_count + 1) * sizeof(pdns_handle_t *)); + } + + if (sdns->client == NULL) + { + sdns->client_count = 0; + return; + } + + sdns->client[sdns->client_count] = c; + sdns->client_count++; + } + closedir(dp); + } + } + + sdns->stattime = 1; +} + +static uint32_t +_pdns_get_default_handles(sdns_handle_t *sdns, pdns_handle_t ***pdns) +{ + int i, j, k, count; + + if (sdns == NULL) return 0; + if (pdns == NULL) return 0; + + count = 0; + + for (i = 0; i < sdns->client_count; i++) + { + if (sdns->client[i]->flags & DNS_FLAG_DEFAULT_RESOLVER) + { + if (count == 0) + { + *pdns = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *)); + } + else + { + *pdns = (pdns_handle_t **)reallocf((*pdns), (count + 1) * sizeof(pdns_handle_t *)); + } + + if (*pdns == NULL) return 0; + + /* Insert sorted by search_order */ + for (j = 0; j < count; j++) + { + if (sdns->client[i]->search_order < (*pdns)[j]->search_order) break; + } + + for (k = count; k > j; k--) (*pdns)[k] = (*pdns)[k-1]; + (*pdns)[j] = sdns->client[i]; + count++; + } + } + + return count; +} + +static uint32_t +_pdns_get_handles_for_name(sdns_handle_t *sdns, const char *name, pdns_handle_t ***pdns) +{ + char *p, *vname; + int i, j, k, count; + + if (sdns == NULL) return 0; + if (pdns == NULL) return 0; + + if (name == NULL) return _pdns_get_default_handles(sdns, pdns); + else if (name[0] == '\0') return _pdns_get_default_handles(sdns, pdns); + + count = 0; + + vname = strdup(name); + i = strlen(vname) - 1; + if ((i >= 0) && (vname[i] == '.')) vname[i] = '\0'; + + p = vname; + while (p != NULL) + { + for (i = 0; i < sdns->client_count; i++) + { + if (sdns->client[i]->name == NULL) continue; + + if (!strcasecmp(sdns->client[i]->name, p)) + { + if (count == 0) + { + *pdns = (pdns_handle_t **)calloc(1, sizeof(pdns_handle_t *)); + } + else + { + *pdns = (pdns_handle_t **)reallocf((*pdns), (count + 1) * sizeof(pdns_handle_t *)); + } + + if (*pdns == NULL) return 0; + + /* Insert sorted by search_order */ + for (j = 0; j < count; j++) + { + if (sdns->client[i]->search_order < (*pdns)[j]->search_order) break; + } + + for (k = count; k > j; k--) (*pdns)[k] = (*pdns)[k-1]; + (*pdns)[j] = sdns->client[i]; + count++; + } + } + + p = strchr(p, '.'); + if (p != NULL) p++; + } + + free(vname); + + if (count != 0) return count; + + return _pdns_get_default_handles(sdns, pdns);; +} + +static void +_pdns_process_res_search_list(pdns_handle_t *pdns) +{ + if (pdns->search_count != SEARCH_COUNT_INIT) return; + for (pdns->search_count = 0; (pdns->res->dnsrch[pdns->search_count] != NULL) && (pdns->res->dnsrch[pdns->search_count][0] != '\0'); pdns->search_count++); +} + +static char * +_pdns_search_list_domain(pdns_handle_t *pdns, uint32_t i) +{ + char *s; + + if (pdns == NULL) return NULL; + if (pdns->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(pdns); + if (i >= pdns->search_count) return NULL; + + s = pdns->search_list[i]; + if (s == NULL) return NULL; + return strdup(s); +} + +static void +_dns_open_notify(sdns_handle_t *sdns) +{ + int status, n; + + if (sdns == NULL) return; + + if (sdns->notify_delay_token == -1) + { + status = notify_register_check(DNS_DELAY_NAME, &(sdns->notify_delay_token)); + if (status != NOTIFY_STATUS_OK) sdns->notify_delay_token = -1; + else status = notify_check(sdns->notify_delay_token, &n); + } + + if (sdns->notify_sys_config_token == -1) + { + status = notify_register_check(dns_configuration_notify_key(), &(sdns->notify_sys_config_token)); + if (status != NOTIFY_STATUS_OK) sdns->notify_sys_config_token = -1; + } + + if (sdns->notify_dir_token == -1) + { + status = notify_register_check(NOTIFY_DIR_NAME, &(sdns->notify_dir_token)); + if (status == NOTIFY_STATUS_OK) + { + status = notify_monitor_file(sdns->notify_dir_token, "/private/etc/resolver", 0); + if (status != NOTIFY_STATUS_OK) + { + notify_cancel(sdns->notify_dir_token); + sdns->notify_dir_token = -1; + } + } + else + { + sdns->notify_dir_token = -1; + } + } +} + +static void +_dns_close_notify(sdns_handle_t *sdns) +{ + if (sdns == NULL) return; + + if (sdns->notify_delay_token != -1) notify_cancel(sdns->notify_delay_token); + sdns->notify_delay_token = -1; + + if (sdns->notify_sys_config_token != -1) notify_cancel(sdns->notify_sys_config_token); + sdns->notify_sys_config_token = -1; + + if (sdns->notify_dir_token != -1) notify_cancel(sdns->notify_dir_token); + sdns->notify_dir_token = -1; +} + +dns_handle_t +dns_open(const char *name) +{ + dns_private_handle_t *dns; + + dns = (dns_private_handle_t *)calloc(1, sizeof(dns_private_handle_t)); + if (dns == NULL) return NULL; + + if (name == NULL) + { + dns->handle_type = DNS_PRIVATE_HANDLE_TYPE_SUPER; + dns->sdns = (sdns_handle_t *)calloc(1, sizeof(sdns_handle_t)); + if (dns->sdns == NULL) + { + free(dns); + return NULL; + } + + dns->sdns->flags |= DNS_FLAG_CHECK_RESOLVER_DIR; + dns->sdns->notify_sys_config_token = -1; + dns->sdns->notify_dir_token = -1; + dns->sdns->notify_delay_token = -1; + _dns_open_notify(dns->sdns); + + return (dns_handle_t)dns; + } + + dns->handle_type = DNS_PRIVATE_HANDLE_TYPE_PLAIN; + + /* Look for name in System Configuration first */ + dns->pdns = _pdns_sc_open(name); + if (dns->pdns == NULL) dns->pdns = _pdns_file_open(name); + + if (dns->pdns == NULL) + { + free(dns); + return NULL; + } + + return (dns_handle_t)dns; +} + +/* + * Release a DNS client handle + */ +void +dns_free(dns_handle_t d) +{ + dns_private_handle_t *dns; + int i; + + if (d == NULL) return; + + dns = (dns_private_handle_t *)d; + + if (dns->recvbuf != NULL) free(dns->recvbuf); + + if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) + { + if (dns->sdns == NULL) return; + + _dns_close_notify(dns->sdns); + + for (i = 0; i < dns->sdns->client_count; i++) + { + _pdns_free(dns->sdns->client[i]); + } + + dns->sdns->client_count = 0; + if (dns->sdns->client != NULL) free(dns->sdns->client); + + free(dns->sdns); + } + else + { + _pdns_free(dns->pdns); + } + + free(dns); +} + +static void +_pdns_debug(pdns_handle_t *pdns, uint32_t flag) +{ + if (pdns == NULL) return; + + if (flag == 0) + { + pdns->res->options &= ~RES_DEBUG; + } + else + { + pdns->res->options |= RES_DEBUG; + } +} + +static void +_sdns_debug(sdns_handle_t *sdns, uint32_t flag) +{ + int i; + + if (sdns == NULL) return; + + if (flag == 0) + { + sdns->flags &= ~ DNS_FLAG_DEBUG; + + for (i = 0; i < sdns->client_count; i++) + { + sdns->client[i]->res->options &= ~RES_DEBUG; + } + } + else + { + sdns->flags |= DNS_FLAG_DEBUG; + + for (i = 0; i < sdns->client_count; i++) + { + sdns->client[i]->res->options |= RES_DEBUG; + } + } +} + +/* + * Enable / Disable debugging + */ +void +dns_set_debug(dns_handle_t d, uint32_t flag) +{ + dns_private_handle_t *dns; + + if (d == NULL) return; + + dns = (dns_private_handle_t *)d; + + if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) + { + _sdns_debug(dns->sdns, flag); + } + else + { + _pdns_debug(dns->pdns, flag); + } +} + +/* + * Returns the number of names in the search list + */ +uint32_t +dns_search_list_count(dns_handle_t d) +{ + dns_private_handle_t *dns; + pdns_handle_t *pdns; + + if (d == NULL) return 0; + + dns = (dns_private_handle_t *)d; + + if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) + { + _check_cache(dns->sdns); + pdns = dns->sdns->pdns_primary; + } + else + { + pdns = dns->pdns; + } + + if (pdns->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(pdns); + return pdns->search_count; +} + +/* + * Returns the domain name at index i in the search list. + * Returns NULL if there are no names in the search list. + * Caller must free the returned name. + */ +char * +dns_search_list_domain(dns_handle_t d, uint32_t i) +{ + dns_private_handle_t *dns; + pdns_handle_t *pdns; + + if (d == NULL) return NULL; + + dns = (dns_private_handle_t *)d; + + if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) + { + _check_cache(dns->sdns); + pdns = dns->sdns->pdns_primary; + } + else + { + pdns = dns->pdns; + } + + return _pdns_search_list_domain(pdns, i); +} + +static int +_pdns_delay(sdns_handle_t *sdns) +{ + int status, n, snooze; + time_t tick; + + if (sdns == NULL) return 0; + + snooze = 0; + n = 0; + + /* No delay if we are not receiving notifications */ + if (sdns->notify_delay_token == -1) return 0; + + if (sdns->dns_delay == 0) + { + status = notify_check(sdns->notify_delay_token, &n); + if ((status == NOTIFY_STATUS_OK) && (n == 1)) + { + /* + * First thread to hit this condition sleeps for DNS_DELAY_INTERVAL seconds + */ + sdns->dns_delay = time(NULL) + DNS_DELAY_INTERVAL; + snooze = DNS_DELAY_INTERVAL; + } + } + else + { + tick = time(NULL); + /* + * Subsequent threads sleep for the remaining duration. + * We add one to round up the interval since our garnularity is coarse. + */ + snooze = 1 + (sdns->dns_delay - tick); + if (snooze < 0) snooze = 0; + } + + if (snooze == 0) return 0; + + sleep(snooze); + + /* When exiting, first thread in resets the delay condition */ + if (n == 1) sdns->dns_delay = 0; + + return 0; +} + +static int +_pdns_query(sdns_handle_t *sdns, pdns_handle_t *pdns, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min) +{ + int n; + + if (name == NULL) return -1; + if (pdns == NULL) return -1; + + if (pdns->flags & DNS_FLAG_FORWARD_TO_MDNSRESPONDER) + { + n = res_query_mDNSResponder(pdns->res, name, class, type, (u_char *)buf, len, from, fromlen); + if ((n < 0) && (min != NULL)) *min = MDNS_MIN_TTL; + return n; + } + + if (pdns->res == NULL) return -1; + if (pdns->res->nscount == 0) return -1; + + if ((type == ns_t_aaaa) && ((pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) == 0) && (pdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA)) return -1; + + _pdns_delay(sdns); + + /* BIND_9 API */ + return res_nquery_soa_min(pdns->res, name, class, type, (u_char *)buf, len, from, (int32_t *)fromlen, min); +} + +__private_extern__ int +_pdns_search(sdns_handle_t *sdns, pdns_handle_t *pdns, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen) +{ + char *dot, *qname; + int append, status; + + if (name == NULL) return -1; + if (pdns == NULL) return -1; + if (pdns->res == NULL) return -1; + if (pdns->res->nscount == 0) return -1; + + if ((type == ns_t_aaaa) && ((pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) == 0) && (pdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA)) return -1; + + qname = NULL; + append = 1; + + /* + * don't append my name if: + * - my name is NULL + * - input name is qualified (i.e. not single component) + * - there is a search list + * - there is a domain name + */ + + if (pdns->name == NULL) append = 0; + + if (append == 1) + { + dot = strrchr(name, '.'); + if (dot != NULL) append = 0; + } + + if (append == 1) + { + if (pdns->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(pdns); + if (pdns->search_count > 0) append = 0; + } + + if ((append == 1) && (pdns->res->defdname != NULL) && (pdns->res->defdname[0] != '\0')) append = 0; + + status = -1; + if (append == 0) + { + /* BIND_9 API */ + _pdns_delay(sdns); + + status = __res_nsearch_list_2(pdns->res, name, class, type, (u_char *)buf, len, from, fromlen, pdns->search_count, pdns->search_list); + } + else + { + _pdns_delay(sdns); + + qname = NULL; + asprintf(&qname, "%s.%s.", name, pdns->name); + if (qname == NULL) return -1; + + /* BIND_9 API */ + status = res_nsearch_2(pdns->res, qname, class, type, (u_char *)buf, len, from, fromlen); + free(qname); + } + + return status; +} + +static int +_sdns_send(sdns_handle_t *sdns, const char *name, uint32_t class, uint32_t type, uint32_t fqdn, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min) +{ + char *qname; + pdns_handle_t **pdns; + uint32_t pdns_count; + int i, n, m; + + pdns = NULL; + pdns_count = 0; + n = -1; + + pdns_count = _pdns_get_handles_for_name(sdns, name, &pdns); + + if (pdns_count == 0) return -1; + + qname = NULL; + asprintf(&qname, "%s%s", name, (fqdn == 0) ? "." : ""); + if (qname == NULL) return -1; + + for (i = 0; i < pdns_count; i++) + { + m = -1; + n = _pdns_query(sdns, pdns[i], qname, class, type, buf, len, from, fromlen, &m); + if (min != NULL) + { + if (*min == -1) *min = m; + else if ((m >= 0) && (m < *min)) *min = m; + } + + if (n > 0) break; + } + + free(pdns); + free(qname); + return n; +} + +__private_extern__ int +_sdns_search(sdns_handle_t *sdns, const char *name, uint32_t class, uint32_t type, uint32_t fqdn, uint32_t recurse, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min) +{ + pdns_handle_t *primary, **pdns; + int i, n, ndots, status, m; + char *dot, *qname; + uint32_t pdns_count; + + if (sdns == NULL) return -1; + if (name == NULL) return -1; + + /* ndots is the threshold for trying a qualified name "as is" */ + ndots = 1; + primary = sdns->pdns_primary; + if ((primary != NULL) && (primary->res != NULL)) ndots = primary->res->ndots; + + /* count dots in input name, and keep track of the location of the last dot */ + n = 0; + dot = NULL; + + for (i = 0; name[i] != '\0'; i++) + { + if (name[i] == '.') + { + n++; + dot = (char *)(name + i); + } + } + + /* the last dot is the last character, name is fully qualified */ + if ((fqdn == 0) && (dot != NULL) && (*(dot + 1) == '\0')) fqdn = 1; + + /* + * If n >= ndots, or it's a FQDN, or if it's a PTR query, + * we try a query with the name "as is". + */ + if ((n >= ndots) || (fqdn == 1) || (type == ns_t_ptr)) + { + status = _sdns_send(sdns, name, class, type, fqdn, buf, len, from, fromlen, min); + if (status > 0) return status; + } + + /* end of the line for FQDNs or PTR queries */ + if (fqdn == 1) return -1; + if (type == ns_t_ptr) return -1; + + if (recurse == 0) return -1; + if (primary == NULL) return -1; + + /* Try appending names from the search list */ + if (primary->search_count == SEARCH_COUNT_INIT) _pdns_process_res_search_list(primary); + n = primary->search_count; + if (n > 0) + { + /* Try qualifying with each name in the search list */ + for (i = 0; i < n ; i++) + { + qname = NULL; + asprintf(&qname, "%s.%s", name, primary->search_list[i]); + if (qname == NULL) return -1; + + m = -1; + status = _sdns_search(sdns, qname, class, type, fqdn, 0, buf, len, from, fromlen, &m); + { + if (*min == -1) *min = m; + else if ((m >= 0) && (m < *min)) *min = m; + } + + free(qname); + if (status > 0) return status; + } + + return -1; + } + + /* + * We get here if the name is not fully qualified (no trailing dot), and there is no search list. + * Try each default client, qualifying with that client's name. + */ + pdns = NULL; + pdns_count = _pdns_get_default_handles(sdns, &pdns); + status = -1; + + if (pdns_count == 0) return -1; + + for (i = 0; i < pdns_count; i++) + { + qname = NULL; + if (pdns[i]->name == NULL) asprintf(&qname, "%s", name); + else asprintf(&qname, "%s.%s", name, pdns[i]->name); + if (qname == NULL) return -1; + + m = -1; + status = _pdns_query(sdns, pdns[i], qname, class, type, buf, len, from, fromlen, min); + { + if (*min == -1) *min = m; + else if ((m >= 0) && (m < *min)) *min = m; + } + + free(qname); + if (status > 0) break; + } + + free(pdns); + return status; +} + +int +dns_query(dns_handle_t d, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen) +{ + dns_private_handle_t *dns; + int status, unused; + + if (d == NULL) return -1; + if (name == NULL) return -1; + dns = (dns_private_handle_t *)d; + + status = -1; + unused = 0; + + if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) + { + _check_cache(dns->sdns); + status = _sdns_search(dns->sdns, name, class, type, 1, 1, buf, len, from, fromlen, &unused); + } + else + { + status = _pdns_query(dns->sdns, dns->pdns, name, class, type, buf, len, from, fromlen, &unused); + } + + return status; +} + + +int +dns_search(dns_handle_t d, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen) +{ + dns_private_handle_t *dns; + int status, unused; + + if (d == NULL) return -1; + if (name == NULL) return -1; + dns = (dns_private_handle_t *)d; + + status = -1; + unused = 0; + + if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) + { + _check_cache(dns->sdns); + status = _sdns_search(dns->sdns, name, class, type, 0, 1, buf, len, from, fromlen, &unused); + } + else + { + status = _pdns_search(dns->sdns, dns->pdns, name, class, type, buf, len, from, fromlen); + } + + return status; +} + +/* + * PRIVATE + */ + +uint32_t +dns_server_list_count(dns_handle_t d) +{ + dns_private_handle_t *dns; + res_state r; + + if (d == NULL) return 0; + dns = (dns_private_handle_t *)d; + + if (dns->handle_type != DNS_PRIVATE_HANDLE_TYPE_PLAIN) return 0; + + if (dns->pdns == NULL) return 0; + + r = dns->pdns->res; + if (r == NULL) return 0; + + return r->nscount; +} + +struct sockaddr * +dns_server_list_address(dns_handle_t d, uint32_t i) +{ + dns_private_handle_t *dns; + res_state r; + struct sockaddr_storage *s; + struct sockaddr *sa; + + if (d == NULL) return NULL; + dns = (dns_private_handle_t *)d; + + if (dns->handle_type != DNS_PRIVATE_HANDLE_TYPE_PLAIN) return NULL; + + if (dns->pdns == NULL) return NULL; + + r = dns->pdns->res; + if (r == NULL) return NULL; + + if (i >= r->nscount) return NULL; + sa = get_nsaddr(r, i); + if (sa == NULL) return NULL; + + s = (struct sockaddr_storage *)calloc(1, sizeof(struct sockaddr_storage)); + if (s == NULL) return NULL; + + memcpy(s, sa, sizeof(struct sockaddr_storage)); + return (struct sockaddr *)s; +} diff --git a/dns.h b/dns.h new file mode 100644 index 0000000..d63803c --- /dev/null +++ b/dns.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 2003 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __DNS_H__ +#define __DNS_H__ + +#include +#include +#include +#include + +/* + * The functions described in this access layer support multiple DNS + * client configurations. Each DNS client has its own set of nameserver + * addresses and its own set of operational parameters. Each client + * can perform DNS queries and searches independent of other clients. + * Each client has a symbolic name which is of the same format as a + * domain name, e.g. "apple.com". A special meta-client, known as the + * "Super" DNS client, acts as a router for DNS queries. The Super + * client chooses among all available clients by finding a best match + * between the domain name given in a query and the names of all known + * clients. + * + * The configuration for a particular client may be read from a file + * having the same format as the traditional "/etc/resolv.conf" file. + * However, client configurations are not limited to being stored in + * files. The implementation of the library may also locate client + * configuratins in other data sources, such as the System Configuration + * Database. Users of this API should make no assumptions about the + * source of the configuration data. + */ + +typedef const struct __dns_handle_private_struct *dns_handle_t; + + +__BEGIN_DECLS + +/* + * Create a client handle for DNS access. + * + * "Super" DNS client + * + * dns_open(NULL) returns a "super" client that routes DNS queries + * among all DNS configurations known to the system. + * + * Queries for qualified names are sent using a client configuration that + * best matches the domain name given in the query. For example, if there + * is a client named "apple.com", a search for "foo.apple.com" would use the + * resolver configuration specified for that client. The matching algorithm + * chooses the client with the maximum number of matching domain components. + * For example, if there are clients named "a.b.c", and "b.c", a search for + * "x.a.b.c" would use the "a.b.c" resolver configuration, while a search + * for "x.y.b.c" would use the "b.c" client. If there are no matches, the + * configuration settings in the default client - generally corresponding to + * the /etc/resolv.conf file or to the "primary" DNS configuration on the + * system are used for the query. + * + * The domain search list defined in the "default" client is used to search + * for unqualified names, by appending each domain in the search list and + * then following the logic for matching qualified names described above. + * + * The DNS access APIs may be used by multiple threads. Each thread must + * use a separate DNS client handle created by calling dns_open(). + * + * A simple DNS client handle may be obtained by providing a non-NULL value + * for the "name" parameter. Simple clients correspond to a single DNS + * configuration, derived from a resolv.conf format file or from some other + * source of configurations known to the system. + * The name parameter may be a full or relative path name (starting with '/' + * or '.'), in which case the client's configuration is read from the + * named file. If the name parameter is not a file path name, the routine + * will search through all known sources of DNS configuration data on the + * system to locate DNS configuration data corresponding to the name supplied, + * or NULL if none can be found. + * + * Use _PATH_RESCONF to open /etc/resolv.conf. + */ +extern dns_handle_t dns_open(const char *name); + +/* + * Close a client and free its resources. + */ +extern void dns_free(dns_handle_t dns); + +/* + * Enable / Disable debug logging. + */ +extern void dns_set_debug(dns_handle_t dns, uint32_t flag); + +/* + * Returns the number of names in the search list. + */ +extern uint32_t dns_search_list_domain_count(dns_handle_t dns); + +/* + * Returns the domain name at index i in the search list. + * Returns NULL if there are no names in the search list, + * or if i is out of range. + * Caller must free the returned value. + */ +extern char *dns_search_list_domain(dns_handle_t dns, uint32_t i); + +/* + * Resolve a name. + * The name is considered fully-qualified (the search list is not used). + * Caller must supply buffer space for the reply message and the server address. + */ +extern int32_t dns_query(dns_handle_t dns, const char *name, uint32_t dnsclass, uint32_t dnstype, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen); + +/* + * Search for a name. + * Caller must supply buffer space for the reply message and the server address. + */ +extern int32_t dns_search(dns_handle_t dns, const char *name, uint32_t dnsclass, uint32_t dnstype, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen); + +__END_DECLS + +#endif /* __DNS_H__ */ diff --git a/dns_async.c b/dns_async.c new file mode 100644 index 0000000..9aa367a --- /dev/null +++ b/dns_async.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_headER_START@ + * + * Portions Copyright (c) 2003 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.1 (the "License"). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. + * + * @APPLE_LICENSE_headER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct +{ + uint32_t datalen; + char *databuf; + uint32_t _size; + uint32_t _dict; + uint32_t _key; + uint32_t _vlist; + uint32_t _val; +} kvbuf_t; + +typedef struct +{ + uint32_t kcount; + const char **key; + uint32_t *vcount; + const char ***val; +} kvdict_t; + +typedef struct +{ + uint32_t count; + uint32_t curr; + kvdict_t *dict; + kvbuf_t *kv; +} kvarray_t; + +extern kvbuf_t *kvbuf_new(void); +extern void kvbuf_add_dict(kvbuf_t *kv); +extern void kvbuf_add_key(kvbuf_t *kv, const char *key); +extern void kvbuf_add_val(kvbuf_t *kv, const char *val); +extern void kvbuf_free(kvbuf_t *kv); +extern void kvarray_free(kvarray_t *kva); +extern uint32_t kvbuf_get_val_len(const char *val); +extern kern_return_t LI_DSLookupGetProcedureNumber(const char *name, int *procno); +extern kern_return_t LI_async_start(mach_port_t *p, uint32_t proc, kvbuf_t *query, void *callback, void *context); +extern kern_return_t LI_async_handle_reply(void *msg, kvarray_t **reply, void **callback, void **context); +extern kern_return_t LI_async_receive(mach_port_t p, kvarray_t **reply); +extern void LI_async_call_cancel(mach_port_t p, void **context); +extern uint32_t kvbuf_get_len(const char *p); + +static kvbuf_t * +dns_make_query(const char *name, uint16_t dnsclass, uint16_t dnstype, uint32_t do_search) +{ + kvbuf_t *request; + char str[128]; + + if (name == NULL) return NULL; + + request = kvbuf_new(); + if (request == NULL) return NULL; + + kvbuf_add_dict(request); + + /* Encode name */ + kvbuf_add_key(request, "domain"); + kvbuf_add_val(request, name); + + /* Encode class */ + snprintf(str, 128, "%hu", dnsclass); + kvbuf_add_key(request, "class"); + kvbuf_add_val(request, str); + + /* Encode type */ + snprintf(str, 128, "%hu", dnstype); + kvbuf_add_key(request, "type"); + kvbuf_add_val(request, str); + + /* Encode do_search */ + snprintf(str, 128, "%hu", do_search); + kvbuf_add_key(request, "search"); + kvbuf_add_val(request, str); + + return request; +} + +int32_t +dns_async_start(mach_port_t *p, const char *name, uint16_t dnsclass, uint16_t dnstype, uint32_t do_search, dns_async_callback callback, void *context) +{ + int32_t status; + kvbuf_t *request; + static int proc = -1; + + *p = MACH_PORT_NULL; + + if (name == NULL) return NO_RECOVERY; + + if (proc < 0) + { + status = LI_DSLookupGetProcedureNumber("dns_proxy", &proc); + if (status != KERN_SUCCESS) return NO_RECOVERY; + } + + request = dns_make_query(name, dnsclass, dnstype, do_search); + if (request == NULL) return NO_RECOVERY; + + status = LI_async_start(p, proc, request, (void *)callback, context); + + kvbuf_free(request); + if (status != 0) return NO_RECOVERY; + return 0; +} + +void +dns_async_cancel(mach_port_t p) +{ + LI_async_call_cancel(p, NULL); +} + +int32_t +dns_async_send(mach_port_t *p, const char *name, uint16_t dnsclass, uint16_t dnstype, uint32_t do_search) +{ + return dns_async_start(p, name, dnsclass, dnstype, do_search, NULL, NULL); +} + +static int +dns_extract_data(kvarray_t *in, char **buf, uint32_t *len, struct sockaddr **from, uint32_t *fromlen) +{ + int32_t status; + struct in_addr addr4; + struct sockaddr_in sin4; + struct in6_addr addr6; + struct sockaddr_in6 sin6; + uint32_t d, k, kcount; + + if (in == NULL) return -1; + if (buf == NULL) return -1; + if (len == NULL) return -1; + if (from == NULL) return -1; + if (fromlen == NULL) return -1; + + *buf = NULL; + *len = 0; + *from = NULL; + *fromlen = 0; + + d = in->curr; + in->curr++; + + if (d >= in->count) return -1; + + kcount = in->dict[d].kcount; + + for (k = 0; k < kcount; k++) + { + if (!strcmp(in->dict[d].key[k], "data")) + { + if (in->dict[d].vcount[k] == 0) continue; + if (*buf != NULL) continue; + + /* + * dns_proxy contains binary data, possibly with embedded nuls, + * so we extract the string length from the kvbuf_t reply that + * Libinfo got from directory services, rather than calling strlen(). + */ + *len = kvbuf_get_len(in->dict[d].val[k][0]); + if (*len == 0) continue; + + *buf = malloc(*len); + if (*buf == NULL) return -1; + + memcpy(*buf, in->dict[d].val[k][0], *len); + } + else if (!strcmp(in->dict[d].key[k], "server")) + { + if (in->dict[d].vcount[k] == 0) continue; + if (*from != NULL) continue; + + memset(&addr4, 0, sizeof(struct in_addr)); + memset(&sin4, 0, sizeof(struct sockaddr_in)); + + memset(&addr6, 0, sizeof(struct in6_addr)); + memset(&sin6, 0, sizeof(struct sockaddr_in6)); + + status = inet_pton(AF_INET6, in->dict[d].val[k][0], &addr6); + if (status == 1) + { + sin6.sin6_addr = addr6; + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + *from = (struct sockaddr *)calloc(1, sin6.sin6_len); + memcpy(*from, &sin6, sin6.sin6_len); + *fromlen = sin6.sin6_len; + } + + status = inet_pton(AF_INET, in->dict[d].val[k][0], &addr4); + if (status == 1) + { + sin4.sin_addr = addr4; + sin4.sin_family = AF_INET; + sin4.sin_len = sizeof(struct sockaddr_in); + *from = (struct sockaddr *)calloc(1, sin4.sin_len); + memcpy(*from, &sin4, sin4.sin_len); + *fromlen = sin4.sin_len; + } + } + } + + return 0; +} + +int32_t +dns_async_receive(mach_port_t p, char **buf, uint32_t *len, struct sockaddr **from, uint32_t *fromlen) +{ + kern_return_t status; + kvarray_t *reply; + + reply = NULL; + + status = LI_async_receive(p, &reply); + if (status != 0) return NO_RECOVERY; + if (reply == NULL) return HOST_NOT_FOUND; + + status = dns_extract_data(reply, buf, len, from, fromlen); + kvarray_free(reply); + if (status != 0) return NO_RECOVERY; + + if (*buf == NULL) return NO_DATA; + + return 0; +} + +int32_t +dns_async_handle_reply(void *msg) +{ + dns_async_callback callback; + void *context; + char *buf; + kvarray_t *reply; + kern_return_t status; + struct sockaddr *from; + uint32_t len, fromlen; + + callback = (dns_async_callback)NULL; + context = NULL; + reply = NULL; + buf = NULL; + len = 0; + from = NULL; + fromlen = 0; + + status = LI_async_handle_reply(msg, &reply, (void **)&callback, &context); + if (status != KERN_SUCCESS) + { + if (status == MIG_REPLY_MISMATCH) return 0; + callback(NO_RECOVERY, NULL, 0, NULL, 0, context); + return NO_RECOVERY; + } + + status = dns_extract_data(reply, &buf, &len, &from, &fromlen); + kvarray_free(reply); + if (status != 0) + { + callback(NO_RECOVERY, NULL, 0, NULL, 0, context); + return 0; + } + + if (buf == NULL) + { + callback(NO_DATA, NULL, 0, NULL, 0, context); + return NO_DATA; + } + + callback(0, buf, len, from, fromlen, context); + + return 0; +} + diff --git a/dns_private.h b/dns_private.h new file mode 100644 index 0000000..1ca315d --- /dev/null +++ b/dns_private.h @@ -0,0 +1,87 @@ +#ifndef __DNS_PRIVATE_H__ +#define __DNS_PRIVATE_H__ + +#include + +#define DNS_FLAG_DEBUG 0x00000001 +#define DNS_FLAG_CHECK_RESOLVER_DIR 0x00000002 +#define DNS_FLAG_HAVE_IPV6_SERVER 0x00000004 +#define DNS_FLAG_OK_TO_SKIP_AAAA 0x00000008 +#define DNS_FLAG_DEFAULT_RESOLVER 0x00000010 +#define DNS_FLAG_FORWARD_TO_MDNSRESPONDER 0x00000020 + +typedef struct +{ + res_state res; + char *source; + char *name; + uint32_t search_count; + char **search_list; + uint16_t port; + uint32_t flags; + uint32_t total_timeout; + uint32_t send_timeout; + uint32_t search_order; + uint32_t reserved1; + void *reserved_pointer1; +} pdns_handle_t; + +typedef struct +{ + pdns_handle_t *pdns_primary; + uint32_t client_count; + pdns_handle_t **client; + uint32_t modtime; + uint32_t stattime; + uint32_t stat_latency; + uint32_t flags; + int notify_sys_config_token; + int notify_dir_token; + int notify_delay_token; + time_t dns_delay; + uint32_t reserved1; + void *reserved_pointer1; +} sdns_handle_t; + +typedef struct __dns_handle_private_struct +{ + uint32_t handle_type; + sdns_handle_t *sdns; + pdns_handle_t *pdns; + char *recvbuf; + uint32_t recvsize; + uint32_t reserved1; + uint32_t reserved2; + void *reserved_pointer1; + void *reserved_pointer2; +} dns_private_handle_t; + + +__BEGIN_DECLS + +/* + * Returns the number of nameserver addresses available to the input + * DNS client. Returns zero if the input handle is a "Super" DNS handle. + */ +extern uint32_t dns_server_list_count(dns_handle_t d); + +/* + * Returns the nameserver address at the given index. Returns NULL + * if the index is out of range. Caller should free the returned sockaddr. + */ +extern struct sockaddr *dns_server_list_address(dns_handle_t d, uint32_t i); + +/* + * Returns a list of all server addresses for all clients. + * Caller must free each list entry, and the returned list. + */ +extern void dns_all_server_addrs(dns_handle_t d, struct sockaddr ***addrs, uint32_t *count); + +/* + * Returns the number of names in the search list. + */ +uint32_t dns_search_list_count(dns_handle_t d); + +__END_DECLS + +#endif /* __DNS_PRIVATE_H__ */ diff --git a/dns_util.c b/dns_util.c new file mode 100644 index 0000000..96b1f28 --- /dev/null +++ b/dns_util.c @@ -0,0 +1,2150 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dns.h" +#include "dns_util.h" +#include "dns_private.h" +#include "res_private.h" + +#define DNS_RESOLVER_DIR "/etc/resolver" + +#define DNS_PRIVATE_HANDLE_TYPE_SUPER 0 +#define DNS_PRIVATE_HANDLE_TYPE_PLAIN 1 +#define DNS_DEFAULT_RECEIVE_SIZE 8192 +#define DNS_MAX_RECEIVE_SIZE 65536 + +#define SDNS_DEFAULT_STAT_LATENCY 10 + +#define DNS_FLAGS_QR_MASK 0x8000 +#define DNS_FLAGS_QR_QUERY 0x0000 + +#define DNS_FLAGS_OPCODE_MASK 0x7800 + +#define DNS_FLAGS_RCODE_MASK 0x000f + +#define DNS_FLAGS_AA 0x0400 +#define DNS_FLAGS_TC 0x0200 +#define DNS_FLAGS_RD 0x0100 +#define DNS_FLAGS_RA 0x0080 + +#define DNS_SOCK_UDP 0 +#define DNS_SOCK_TCP_UNCONNECTED 1 +#define DNS_SOCK_TCP_CONNECTED 2 + +#define INET_NTOP_AF_INET_OFFSET 4 +#define INET_NTOP_AF_INET6_OFFSET 8 + +#define MAXPACKET 1024 + +extern void res_client_close(res_state res); +extern int __res_nquery(res_state statp, const char *name, int class, int type, u_char *answer, int anslen); +extern int dns_res_send(res_state statp, const u_char *buf, int buflen, u_char *ans, int *anssiz, struct sockaddr *from, int *fromlen); +extern void _check_cache(sdns_handle_t *sdns); +extern int _sdns_search(sdns_handle_t *sdns, const char *name, uint32_t class, uint32_t type, uint32_t fqdn, uint32_t recurse, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen, int *min); +extern int _pdns_search(sdns_handle_t *sdns, pdns_handle_t *pdns, const char *name, uint32_t class, uint32_t type, char *buf, uint32_t len, struct sockaddr *from, uint32_t *fromlen); +static pthread_mutex_t _dnsPrintLock = PTHREAD_MUTEX_INITIALIZER; + +static void +_dns_print_lock(void) +{ + pthread_mutex_lock(&_dnsPrintLock); +} + +static void +_dns_print_unlock(void) +{ + pthread_mutex_unlock(&_dnsPrintLock); +} + + +static uint8_t +_dns_parse_uint8(char **p) +{ + uint8_t v; + + v = (uint8_t)**p; + *p += 1; + return v; +} + +static uint16_t +_dns_parse_uint16(char **p) +{ + uint16_t *x, v; + + x = (uint16_t *)*p; + v = ntohs(*x); + *p += 2; + return v; +} + +static uint32_t +_dns_parse_uint32(char **p) +{ + uint32_t *x, v; + + x = (uint32_t *)*p; + v = ntohl(*x); + *p += 4; + return v; +} + +static uint8_t +_dns_cname_length(char *s) +{ + uint8_t l; + + if (s == NULL) return 1; + l = strlen(s); + while ((s[l - 1] == '.') && (l > 1)) l--; + return l; +} + +static void +_dns_insert_cname(char *s, char *p) +{ + int i; + uint8_t len, dlen; + + if (s == NULL) + { + *p = 0; + return; + } + + if (!strcmp(s, ".")) + { + p[0] = 1; + p[1] = '.'; + p[2] = 0; + return; + } + + len = _dns_cname_length(s); + + p[0] = '.'; + memmove(p + 1, s, len); + p[len + 1] = '.'; + + dlen = 0; + + for (i = len + 1; i >= 0; i--) + { + if (p[i] == '.') + { + p[i] = dlen; + dlen = 0; + } + else dlen++; + } +} + +static char * +_dns_parse_string(const char *p, char **x, int32_t *remaining) +{ + char *str; + uint8_t len; + + if (*remaining < 1) return NULL; + *remaining -= 1; + + len = (uint8_t)**x; + *x += 1; + + if (*remaining < len) return NULL; + *remaining -= len; + + str = malloc(len + 1); + memmove(str, *x, len); + str[len] = '\0'; + *x += len; + + return str; +} + +static char * +_dns_parse_domain_name(const char *p, char **x, int32_t *remaining) +{ + uint8_t *v8; + uint16_t *v16, skip; + uint16_t i, j, dlen, len; + int more, compressed; + char *name, *start, *y, *z; + + if (*remaining < 1) return NULL; + + z = *x + *remaining; + start = *x; + compressed = 0; + more = 1; + name = malloc(1); + name[0] = '\0'; + len = 1; + j = 0; + skip = 0; + + while (more == 1) + { + if ((*x + 1) > z) + { + free(name); + return NULL; + } + + v8 = (uint8_t *)*x; + dlen = *v8; + + if ((dlen & 0xc0) == 0xc0) + { + if ((*x + 2) > z) + { + free(name); + return NULL; + } + + v16 = (uint16_t *)*x; + + y = (char *)p + (ntohs(*v16) & 0x3fff); + if ((*x == y) || (y > z)) + { + free(name); + return NULL; + } + + *x = y; + if (compressed == 0) skip += 2; + compressed = 1; + continue; + } + + if ((*x + 1) > z) + { + free(name); + return NULL; + } + + *x += 1; + + if (dlen > 0) + { + len += dlen; + name = realloc(name, len); + } + + if ((*x + dlen) > z) + { + free(name); + return NULL; + } + + for (i = 0; i < dlen; i++) + { + name[j++] = **x; + *x += 1; + } + + name[j] = '\0'; + if (compressed == 0) skip += (dlen + 1); + + if (dlen == 0) more = 0; + else + { + if ((*x + 1) > z) + { + free(name); + return NULL; + } + + v8 = (uint8_t *)*x; + if (*v8 != 0) + { + len += 1; + name = realloc(name, len); + name[j++] = '.'; + name[j] = '\0'; + } + } + } + + if ((start + skip) > z) + { + free(name); + return NULL; + } + + *x = start + skip; + *remaining -= skip; + + return name; +} + +dns_resource_record_t * +_dns_parse_resource_record_internal(const char *p, char **x, int32_t *remaining) +{ + uint32_t size, bx, mi; + uint16_t rdlen; + uint8_t byte, i; + dns_resource_record_t *r; + char *eor; + + if (*remaining < 1) return NULL; + + r = (dns_resource_record_t *)calloc(1, sizeof(dns_resource_record_t)); + + r->name = _dns_parse_domain_name(p, x, remaining); + if (r->name == NULL) + { + free(r); + return NULL; + } + + if (*remaining < 10) + { + free(r); + return NULL; + } + + r->dnstype = _dns_parse_uint16(x); + r->dnsclass = _dns_parse_uint16(x); + r->ttl = _dns_parse_uint32(x); + rdlen = _dns_parse_uint16(x); + + *remaining -= 10; + + eor = *x; + r->data.A = NULL; + + switch (r->dnstype) + { + case ns_t_a: + if (*remaining < 4) + { + free(r); + return NULL; + } + + *remaining -= 4; + + size = sizeof(dns_address_record_t); + r->data.A = (dns_address_record_t *)calloc(1, size); + r->data.A->addr.s_addr = htonl(_dns_parse_uint32(x)); + break; + + case ns_t_aaaa: + if (*remaining < 16) + { + free(r); + return NULL; + } + + *remaining -= 16; + + size = sizeof(dns_in6_address_record_t); + r->data.AAAA = (dns_in6_address_record_t *)calloc(1, size); + r->data.AAAA->addr.__u6_addr.__u6_addr32[0] = htonl(_dns_parse_uint32(x)); + r->data.AAAA->addr.__u6_addr.__u6_addr32[1] = htonl(_dns_parse_uint32(x)); + r->data.AAAA->addr.__u6_addr.__u6_addr32[2] = htonl(_dns_parse_uint32(x)); + r->data.AAAA->addr.__u6_addr.__u6_addr32[3] = htonl(_dns_parse_uint32(x)); + break; + + case ns_t_ns: + case ns_t_md: + case ns_t_mf: + case ns_t_cname: + case ns_t_mb: + case ns_t_mg: + case ns_t_mr: + case ns_t_ptr: + size = sizeof(dns_domain_name_record_t); + r->data.CNAME = (dns_domain_name_record_t *)calloc(1,size); + r->data.CNAME->name = _dns_parse_domain_name(p, x, remaining); + if (r->data.CNAME->name == NULL) + { + free(r->data.CNAME); + free(r); + return NULL; + } + break; + + case ns_t_soa: + size = sizeof(dns_SOA_record_t); + r->data.SOA = (dns_SOA_record_t *)calloc(1, size); + + r->data.SOA->mname = _dns_parse_domain_name(p, x, remaining); + if (r->data.SOA->mname == NULL) + { + free(r->data.SOA); + free(r); + return NULL; + } + + r->data.SOA->rname = _dns_parse_domain_name(p, x, remaining); + if (r->data.SOA->rname == NULL) + { + free(r->data.SOA->mname); + free(r->data.SOA); + free(r); + return NULL; + } + + if (*remaining < 20) + { + free(r->data.SOA->mname); + free(r->data.SOA->rname); + free(r->data.SOA); + free(r); + return NULL; + } + + *remaining -= 20; + + r->data.SOA->serial = _dns_parse_uint32(x); + r->data.SOA->refresh = _dns_parse_uint32(x); + r->data.SOA->retry = _dns_parse_uint32(x); + r->data.SOA->expire = _dns_parse_uint32(x); + r->data.SOA->minimum = _dns_parse_uint32(x); + break; + + case ns_t_wks: + if (*remaining < 5) + { + free(r); + return NULL; + } + + *remaining -= rdlen; + + size = sizeof(dns_WKS_record_t); + r->data.WKS = (dns_WKS_record_t *)calloc(1, size); + + r->data.WKS->addr.s_addr = htonl(_dns_parse_uint32(x)); + r->data.WKS->protocol = _dns_parse_uint8(x); + size = rdlen - 5; + r->data.WKS->maplength = size * 8; + r->data.WKS->map = NULL; + if (size == 0) break; + + r->data.WKS->map = (uint8_t *)calloc(1, r->data.WKS->maplength); + mi = 0; + for (bx = 0; bx < size; bx++) + { + byte = _dns_parse_uint8(x); + for (i = 128; i >= 1; i = i/2) + { + if (byte & i) r->data.WKS->map[mi] = 0xff; + else r->data.WKS->map[mi] = 0; + mi++; + } + } + break; + + case ns_t_hinfo: + size = sizeof(dns_HINFO_record_t); + r->data.HINFO = (dns_HINFO_record_t *)calloc(1, size); + + r->data.HINFO->cpu = _dns_parse_string(p, x, remaining); + if (r->data.HINFO->cpu == NULL) + { + free(r->data.HINFO); + free(r); + return NULL; + } + + r->data.HINFO->os = _dns_parse_string(p, x, remaining); + if (r->data.HINFO->os == NULL) + { + free(r->data.HINFO->cpu); + free(r->data.HINFO); + free(r); + return NULL; + } + + break; + + case ns_t_minfo: + size = sizeof(dns_MINFO_record_t); + r->data.MINFO = (dns_MINFO_record_t *)calloc(1, size); + + r->data.MINFO->rmailbx = _dns_parse_domain_name(p, x, remaining); + if (r->data.MINFO->rmailbx == NULL) + { + free(r->data.MINFO); + free(r); + return NULL; + } + + r->data.MINFO->emailbx = _dns_parse_domain_name(p, x, remaining); + if (r->data.MINFO->emailbx == NULL) + { + free(r->data.MINFO->rmailbx); + free(r->data.MINFO); + free(r); + return NULL; + } + + break; + + case ns_t_mx: + if (*remaining < 2) + { + free(r); + return NULL; + } + + *remaining -= 2; + + size = sizeof(dns_MX_record_t); + r->data.MX = (dns_MX_record_t *)calloc(1, size); + + r->data.MX->preference = _dns_parse_uint16(x); + r->data.MX->name = _dns_parse_domain_name(p, x, remaining); + if (r->data.MX->name == NULL) + { + free(r->data.MX); + free(r); + return NULL; + } + + break; + + case ns_t_txt: + size = sizeof(dns_TXT_record_t); + r->data.TXT = (dns_TXT_record_t *)malloc(size); + r->data.TXT->string_count = 0; + r->data.TXT->strings = NULL; + + while (*x < (eor + rdlen)) + { + if (r->data.TXT->string_count == 0) + { + r->data.TXT->strings = (char **)calloc(1, sizeof(char *)); + } + else + { + r->data.TXT->strings = (char **)realloc(r->data.TXT->strings, (r->data.TXT->string_count + 1) * sizeof(char *)); + } + + r->data.TXT->strings[r->data.TXT->string_count] = _dns_parse_string(p, x, remaining); + if (r->data.TXT->strings[r->data.TXT->string_count] == NULL) + { + free(r->data.TXT->strings); + free(r->data.TXT); + free(r); + return NULL; + } + r->data.TXT->string_count++; + } + + break; + + case ns_t_rp: + size = sizeof(dns_RP_record_t); + r->data.RP = (dns_RP_record_t *)calloc(1, size); + + r->data.RP->mailbox = _dns_parse_domain_name(p, x, remaining); + if (r->data.RP->mailbox == NULL) + { + free(r->data.RP); + free(r); + return NULL; + } + + r->data.RP->txtdname = _dns_parse_domain_name(p, x, remaining); + if (r->data.RP->txtdname == NULL) + { + free(r->data.RP->mailbox); + free(r->data.RP); + free(r); + return NULL; + } + + break; + + case ns_t_afsdb: + if (*remaining < 4) + { + free(r); + return NULL; + } + + *remaining -= 4; + size = sizeof(dns_AFSDB_record_t); + r->data.AFSDB = (dns_AFSDB_record_t *)calloc(1, size); + + r->data.AFSDB->subtype = _dns_parse_uint32(x); + r->data.AFSDB->hostname = _dns_parse_domain_name(p, x, remaining); + if (r->data.AFSDB->hostname == NULL) + { + free(r->data.AFSDB); + free(r); + return NULL; + } + + break; + + case ns_t_x25: + size = sizeof(dns_X25_record_t); + r->data.X25 = (dns_X25_record_t *)calloc(1, size); + + r->data.X25->psdn_address = _dns_parse_string(p, x, remaining); + if (r->data.X25->psdn_address == NULL) + { + free(r->data.X25); + free(r); + return NULL; + } + + break; + + case ns_t_isdn: + size = sizeof(dns_ISDN_record_t); + r->data.ISDN = (dns_ISDN_record_t *)calloc(1, size); + + r->data.ISDN->isdn_address = _dns_parse_string(p, x, remaining); + if (r->data.ISDN->isdn_address == NULL) + { + free(r->data.ISDN); + free(r); + return NULL; + } + + if (*x < (eor + rdlen)) + { + r->data.ISDN->subaddress = _dns_parse_string(p, x, remaining); + if (r->data.ISDN->subaddress == NULL) + { + free(r->data.ISDN->isdn_address); + free(r->data.ISDN); + free(r); + return NULL; + } + } + else + { + r->data.ISDN->subaddress = NULL; + } + + break; + + case ns_t_rt: + if (*remaining < 2) + { + free(r); + return NULL; + } + + *remaining -= 2; + + size = sizeof(dns_RT_record_t); + r->data.RT = (dns_RT_record_t *)calloc(1, size); + + r->data.RT->preference = _dns_parse_uint16(x); + r->data.RT->intermediate = _dns_parse_domain_name(p, x, remaining); + if (r->data.RT->intermediate == NULL) + { + free(r->data.RT); + free(r); + return NULL; + } + + break; + + case ns_t_loc: + if (*remaining < 16) + { + free(r); + return NULL; + } + + *remaining -= 16; + + size = sizeof(dns_LOC_record_t); + r->data.LOC = (dns_LOC_record_t *)calloc(1, size); + + r->data.LOC->version = _dns_parse_uint8(x); + r->data.LOC->size = _dns_parse_uint8(x); + r->data.LOC->horizontal_precision = _dns_parse_uint8(x); + r->data.LOC->vertical_precision = _dns_parse_uint8(x); + r->data.LOC->latitude = _dns_parse_uint32(x); + r->data.LOC->longitude = _dns_parse_uint32(x); + r->data.LOC->altitude = _dns_parse_uint32(x); + break; + + case ns_t_srv: + if (*remaining < 6) + { + free(r); + return NULL; + } + + *remaining -= 6; + + size = sizeof(dns_SRV_record_t); + r->data.SRV = (dns_SRV_record_t *)calloc(1, size); + + r->data.SRV->priority = _dns_parse_uint16(x); + r->data.SRV->weight = _dns_parse_uint16(x); + r->data.SRV->port = _dns_parse_uint16(x); + r->data.SRV->target = _dns_parse_domain_name(p, x, remaining); + if (r->data.SRV->target == NULL) + { + free(r->data.SRV); + free(r); + return NULL; + } + + break; + + case ns_t_null: + default: + if (*remaining < rdlen) + { + free(r); + return NULL; + } + + *remaining -= rdlen; + + size = sizeof(dns_raw_resource_record_t); + r->data.DNSNULL = (dns_raw_resource_record_t *)calloc(1, size); + + r->data.DNSNULL->length = rdlen; + r->data.DNSNULL->data = calloc(1, rdlen); + memmove(r->data.DNSNULL->data, *x, rdlen); + *x += rdlen; + break; + } + + *x = eor + rdlen; + return r; +} + +dns_resource_record_t * +dns_parse_resource_record(const char *buf, uint32_t len) +{ + char *x; + int32_t remaining; + + remaining = len; + x = (char *)buf; + return _dns_parse_resource_record_internal(buf, &x, &remaining); +} + +dns_question_t * +_dns_parse_question_internal(const char *p, char **x, int32_t *remaining) +{ + dns_question_t *q; + + if (x == NULL) return NULL; + if (*x == NULL) return NULL; + if (*remaining < 1) return NULL; + + q = (dns_question_t *)calloc(1, sizeof(dns_question_t)); + + q->name = _dns_parse_domain_name(p, x, remaining); + if (q->name == NULL) + { + free(q); + return NULL; + } + + if (*remaining < 4) + { + free(q->name); + free(q); + return NULL; + } + + *remaining = *remaining - 4; + + q->dnstype = _dns_parse_uint16(x); + q->dnsclass = _dns_parse_uint16(x); + + return q; +} + +dns_question_t * +dns_parse_question(const char *buf, uint32_t len) +{ + char *x; + int32_t remaining; + + remaining = len; + x = (char *)buf; + return _dns_parse_question_internal(buf, &x, &remaining); +} + + +dns_reply_t * +dns_parse_packet(const char *p, uint32_t len) +{ + dns_reply_t *r; + dns_header_t *h; + char *x; + uint32_t i, size; + int32_t remaining; + + if (p == NULL) return NULL; + if (len < NS_HFIXEDSZ) return NULL; + + x = (char *)p; + + r = (dns_reply_t *)calloc(1, sizeof(dns_reply_t)); + + r->header = (dns_header_t *)calloc(1, sizeof(dns_header_t)); + h = r->header; + + h->xid = _dns_parse_uint16(&x); + h->flags = _dns_parse_uint16(&x); + h->qdcount = _dns_parse_uint16(&x); + h->ancount = _dns_parse_uint16(&x); + h->nscount = _dns_parse_uint16(&x); + h->arcount = _dns_parse_uint16(&x); + + remaining = len - NS_HFIXEDSZ; + + size = sizeof(dns_question_t *); + r->question = (dns_question_t **)calloc(h->qdcount, size); + for (i = 0; i < h->qdcount; i++) + { + r->question[i] = _dns_parse_question_internal(p, &x, &remaining); + if (r->question[i] ==NULL) + { + h->qdcount = 0; + if (i > 0) h->qdcount = i - 1; + h->ancount = 0; + h->nscount = 0; + h->arcount = 0; + dns_free_reply(r); + return NULL; + } + } + + size = sizeof(dns_resource_record_t *); + + r->answer = (dns_resource_record_t **)calloc(h->ancount, size); + for (i = 0; i < h->ancount; i++) + { + r->answer[i] = _dns_parse_resource_record_internal(p, &x, &remaining); + if (r->answer[i] == NULL) + { + h->ancount = 0; + if (i > 0) h->ancount = i - 1; + h->nscount = 0; + h->arcount = 0; + dns_free_reply(r); + return NULL; + } + } + + r->authority = (dns_resource_record_t **)calloc(h->nscount, size); + for (i = 0; i < h->nscount; i++) + { + r->authority[i] = _dns_parse_resource_record_internal(p, &x, &remaining); + if (r->authority[i] == NULL) + { + h->nscount = 0; + if (i > 0) h->nscount = i - 1; + h->arcount = 0; + dns_free_reply(r); + return NULL; + } + } + + r->additional = (dns_resource_record_t **)calloc(h->arcount, size); + for (i = 0; i < h->arcount; i++) + { + r->additional[i] = _dns_parse_resource_record_internal(p, &x, &remaining); + if (r->additional[i] == NULL) + { + h->arcount = 0; + if (i > 0) h->arcount = i - 1; + dns_free_reply(r); + return NULL; + } + } + + return r; +} + +void +dns_free_resource_record(dns_resource_record_t *r) +{ + int i; + + free(r->name); + + switch (r->dnstype) + { + case ns_t_a: + free(r->data.A); + break; + + case ns_t_aaaa: + free(r->data.AAAA); + break; + + case ns_t_ns: + case ns_t_md: + case ns_t_mf: + case ns_t_cname: + case ns_t_mb: + case ns_t_mg: + case ns_t_mr: + case ns_t_ptr: + free(r->data.CNAME->name); + free(r->data.CNAME); + break; + + case ns_t_soa: + free(r->data.SOA->mname); + free(r->data.SOA->rname); + free(r->data.SOA); + break; + + case ns_t_wks: + free(r->data.WKS->map); + free(r->data.WKS); + break; + + case ns_t_hinfo: + free(r->data.HINFO->cpu); + free(r->data.HINFO->os); + free(r->data.HINFO); + break; + + case ns_t_minfo: + free(r->data.MINFO->rmailbx); + free(r->data.MINFO->emailbx); + free(r->data.MINFO); + break; + + case ns_t_mx: + free(r->data.MX->name); + free(r->data.MX); + break; + + + case ns_t_txt: + for (i=0; idata.TXT->string_count; i++) + { + free(r->data.TXT->strings[i]); + } + if (r->data.TXT->strings != NULL) + free(r->data.TXT->strings); + free(r->data.TXT); + break; + + case ns_t_rp: + free(r->data.RP->mailbox); + free(r->data.RP->txtdname); + free(r->data.RP); + break; + + case ns_t_afsdb: + free(r->data.AFSDB->hostname); + free(r->data.AFSDB); + break; + + case ns_t_x25: + free(r->data.X25->psdn_address); + free(r->data.X25); + break; + + case ns_t_isdn: + free(r->data.ISDN->isdn_address); + if (r->data.ISDN->subaddress != NULL) + free(r->data.ISDN->subaddress); + free(r->data.ISDN); + break; + + case ns_t_rt: + free(r->data.RT->intermediate); + free(r->data.RT); + break; + + case ns_t_loc: + free(r->data.LOC); + break; + + case ns_t_srv: + free(r->data.SRV->target); + free(r->data.SRV); + break; + + case ns_t_null: + default: + free(r->data.DNSNULL->data); + free(r->data.DNSNULL); + break; + } + + free(r); +} + +void +dns_free_reply(dns_reply_t *r) +{ + uint32_t i; + + if (r == NULL) return; + if (r->header != NULL) + { + for (i = 0; i < r->header->qdcount; i++) + { + free(r->question[i]->name); + free(r->question[i]); + } + + for (i = 0; i < r->header->ancount; i++) dns_free_resource_record(r->answer[i]); + for (i = 0; i < r->header->nscount; i++) dns_free_resource_record(r->authority[i]); + for (i = 0; i < r->header->arcount; i++) dns_free_resource_record(r->additional[i]); + + free(r->header); + } + + if (r->question != NULL) free(r->question); + if (r->answer != NULL) free(r->answer); + if (r->authority != NULL) free(r->authority); + if (r->additional != NULL) free(r->additional); + + if (r->server != NULL) free(r->server); + + free(r); +} + +static void +_dns_append_question(dns_question_t *q, char **s, uint16_t *l) +{ + uint16_t len, *p; + char *x; + + if (q == NULL) return; + + len = *l + _dns_cname_length(q->name) + 2 + 4; + *s = realloc(*s, len); + + _dns_insert_cname(q->name, (char *)*s + *l); + *l = len; + + x = *s + (len - 4); + + p = (uint16_t *)x; + *p = htons(q->dnstype); + x += 2; + + p = (uint16_t *)x; + *p = htons(q->dnsclass); + +} + +static void +_dns_append_resource_record(dns_resource_record_t *r, char **s, uint16_t *l) +{ + uint16_t clen, len, *p, extra, rdlen; + uint32_t *p2; + char *x; + + if (r == NULL) return; + + extra = 10; + switch (r->dnstype) + { + case ns_t_a: + extra += 4; + break; + case ns_t_ptr: + extra += 2; + clen = _dns_cname_length(r->data.PTR->name); + extra += clen; + break; + default: break; + } + + len = *l + _dns_cname_length(r->name) + 2 + extra; + *s = realloc(*s, len); + + _dns_insert_cname(r->name, (char *)*s + *l); + *l = len; + + x = *s + (len - extra); + + p = (uint16_t *)x; + *p = htons(r->dnstype); + x += 2; + + p = (uint16_t *)x; + *p = htons(r->dnsclass); + x += 2; + + p2 = (uint32_t *)x; + *p2 = htonl(r->ttl); + x += 4; + + switch (r->dnstype) + { + case ns_t_a: + rdlen = 4; + p = (uint16_t *)x; + *p = htons(rdlen); + x += 2; + + p2 = (uint32_t *)x; + *p2 = htons(r->data.A->addr.s_addr); + x += 4; + return; + + case ns_t_ptr: + clen = _dns_cname_length(r->data.PTR->name) + 2; + p = (uint16_t *)x; + *p = htons(clen); + x += 2; + _dns_insert_cname(r->data.PTR->name, x); + x += clen; + return; + + default: return; + } +} + +char * +dns_build_reply(dns_reply_t *dnsr, uint16_t *rl) +{ + uint16_t i, len; + dns_header_t *h; + char *s, *x; + + if (dnsr == NULL) return NULL; + + len = NS_HFIXEDSZ; + + s = malloc(len); + x = s + len; + + memset(s, 0, len); + *rl = len; + + h = (dns_header_t *)s; + + h->xid = htons(dnsr->header->xid); + h->flags = htons(dnsr->header->flags); + h->qdcount = htons(dnsr->header->qdcount); + h->ancount = htons(dnsr->header->ancount); + h->nscount = htons(dnsr->header->nscount); + h->arcount = htons(dnsr->header->arcount); + + for (i = 0; i < dnsr->header->qdcount; i++) + { + _dns_append_question(dnsr->question[i], &s, rl); + } + + for (i = 0; i < dnsr->header->ancount; i++) + { + _dns_append_resource_record(dnsr->answer[i], &s, rl); + } + + for (i = 0; i < dnsr->header->nscount; i++) + { + _dns_append_resource_record(dnsr->authority[i], &s, rl); + } + + for (i = 0; i < dnsr->header->arcount; i++) + { + _dns_append_resource_record(dnsr->additional[i], &s, rl); + } + + return s; +} + +void +dns_free_question(dns_question_t *q) +{ + if (q == NULL) return; + if (q->name != NULL) free(q->name); + free(q); +} + +void +dns_set_buffer_size(dns_handle_t d, uint32_t len) +{ + dns_private_handle_t *dns; + if (d == NULL) return; + + dns = (dns_private_handle_t *)d; + if (dns->recvsize == len) return; + + if (dns->recvbuf != NULL) + { + free(dns->recvbuf); + dns->recvbuf = NULL; + } + + dns->recvsize = len; + if (dns->recvsize > DNS_MAX_RECEIVE_SIZE) dns->recvsize = DNS_MAX_RECEIVE_SIZE; + + if (dns->recvsize > 0) dns->recvbuf = malloc(dns->recvsize); +} + +uint32_t +dns_get_buffer_size(dns_handle_t d) +{ + dns_private_handle_t *dns; + if (d == NULL) return 0; + + dns = (dns_private_handle_t *)d; + return dns->recvsize; +} + +dns_reply_t * +dns_lookup_soa_min(dns_handle_t d, const char *name, uint32_t class, uint32_t type, int *min) +{ + dns_private_handle_t *dns; + dns_reply_t *r; + int len; + struct sockaddr_storage *from; + uint32_t fromlen; + + if (d == NULL) return NULL; + if (name == NULL) return NULL; + + dns = (dns_private_handle_t *)d; + if (min != NULL) *min = -1; + + if (dns->recvbuf == NULL) + { + if (dns->recvsize == 0) dns->recvsize = DNS_DEFAULT_RECEIVE_SIZE; + + dns->recvbuf = malloc(dns->recvsize); + if (dns->recvbuf == NULL) return NULL; + } + + fromlen = sizeof(struct sockaddr_storage); + from = (struct sockaddr_storage *)calloc(1, sizeof(struct sockaddr_storage)); + len = -1; + + if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) + { + _check_cache(dns->sdns); + len = _sdns_search(dns->sdns, name, class, type, 0, 1, dns->recvbuf, dns->recvsize, (struct sockaddr *)from, &fromlen, min); + } + else + { + /* NB. Minumium SOA TTL values are NOT provided when the caller passes a DNS_PRIVATE_HANDLE_TYPE_PLAIN handle */ + len = _pdns_search(dns->sdns, dns->pdns, name, class, type, dns->recvbuf, dns->recvsize, (struct sockaddr *)from, &fromlen); + } + + if (len <= 0) + { + free(from); + return NULL; + } + + r = dns_parse_packet(dns->recvbuf, len); + + if (r == NULL) free(from); + else r->server = (struct sockaddr *)from; + + return r; +} + +dns_reply_t * +dns_lookup(dns_handle_t d, const char *name, uint32_t class, uint32_t type) +{ + int unused = 0; + + return dns_lookup_soa_min(d, name, class, type, &unused); +} + +/* + * DNS printing utilities + */ + +static char * +coord_ntoa(int32_t coord, uint32_t islat) +{ + int32_t deg, min, sec, secfrac; + static char buf[64]; + char dir; + + coord = coord - 0x80000000; + dir = 'N'; + + if ((islat == 1) && (coord < 0)) + { + dir = 'S'; + coord = -coord; + } + + if (islat == 0) + { + dir = 'E'; + if (coord < 0) + { + dir = 'W'; + coord = -coord; + } + } + + secfrac = coord % 1000; + coord = coord / 1000; + sec = coord % 60; + coord = coord / 60; + min = coord % 60; + coord = coord / 60; + deg = coord; + + sprintf(buf, "%d %.2d %.2d.%.3d %c", deg, min, sec, secfrac, dir); + return buf; +} + +static char * +alt_ntoa(int32_t alt) +{ + int32_t ref, m, frac, sign; + static char buf[128]; + + ref = 100000 * 100; + sign = 1; + + if (alt < ref) + { + alt = ref - alt; + sign = -1; + } + else + { + alt = alt - ref; + } + + frac = alt % 100; + m = (alt / 100) * sign; + + sprintf(buf, "%d.%.2d", m, frac); + return buf; +} + +static unsigned int +poweroften[10] = +{ 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000 +}; + +static char * +precsize_ntoa(uint8_t prec) +{ + static char buf[19]; + unsigned long val; + int mantissa, exponent; + + mantissa = (int)((prec >> 4) & 0x0f) % 10; + exponent = (int)((prec >> 0) & 0x0f) % 10; + + val = mantissa * poweroften[exponent]; + + sprintf(buf, "%ld.%.2ld", val/100, val%100); + return buf; +} + +const char * +dns_type_string(uint16_t t) +{ + switch (t) + { + case ns_t_a: return "A "; + case ns_t_ns: return "NS "; + case ns_t_md: return "MD "; + case ns_t_mf: return "MF "; + case ns_t_cname: return "CNAME"; + case ns_t_soa: return "SOA "; + case ns_t_mb: return "MB "; + case ns_t_mg: return "MG "; + case ns_t_mr: return "MR "; + case ns_t_null: return "NULL "; + case ns_t_wks: return "WKS "; + case ns_t_ptr: return "PTR "; + case ns_t_hinfo: return "HINFO"; + case ns_t_minfo: return "MINFO"; + case ns_t_mx: return "MX "; + case ns_t_txt: return "TXT "; + case ns_t_rp: return "PR "; + case ns_t_afsdb: return "AFSDB"; + case ns_t_x25: return "X25 "; + case ns_t_isdn: return "ISDN "; + case ns_t_rt: return "RT "; + case ns_t_nsap: return "NSAP "; + case ns_t_nsap_ptr: return "NSPTR"; + case ns_t_sig: return "SIG "; + case ns_t_key: return "KEY "; + case ns_t_px: return "PX "; + case ns_t_gpos: return "GPOS "; + case ns_t_aaaa: return "AAAA "; + case ns_t_loc: return "LOC "; + case ns_t_nxt: return "NXT "; + case ns_t_eid: return "EID "; + case ns_t_nimloc: return "NIMLC"; + case ns_t_srv: return "SRV "; + case ns_t_atma: return "ATMA "; + case ns_t_naptr: return "NAPTR"; + case ns_t_kx: return "KX "; + case ns_t_cert: return "CERT "; + case ns_t_a6: return "A6 "; + case ns_t_dname: return "DNAME"; + case ns_t_sink: return "SINK "; + case ns_t_opt: return "OPT "; + case ns_t_tkey: return "TKEY "; + case ns_t_tsig: return "TSIG "; + case ns_t_ixfr: return "IXFR "; + case ns_t_axfr: return "AXFR "; + case ns_t_mailb: return "MAILB"; + case ns_t_maila: return "MAILA"; + case ns_t_any: return "ANY "; + case ns_t_zxfr: return "ZXFR "; + default: return "?????"; + } + + return "?????"; +} + +int32_t +dns_type_number(const char *t, uint16_t *n) +{ + if (t == NULL) return -1; + + if (!strcasecmp(t, "A")) { *n = ns_t_a; return 0; } + if (!strcasecmp(t, "NS")) { *n = ns_t_ns; return 0; } + if (!strcasecmp(t, "MD")) { *n = ns_t_md; return 0; } + if (!strcasecmp(t, "MF")) { *n = ns_t_mf; return 0; } + if (!strcasecmp(t, "CNAME")) { *n = ns_t_cname; return 0; } + if (!strcasecmp(t, "SOA")) { *n = ns_t_soa; return 0; } + if (!strcasecmp(t, "MB")) { *n = ns_t_mb; return 0; } + if (!strcasecmp(t, "MG")) { *n = ns_t_mg; return 0; } + if (!strcasecmp(t, "MR")) { *n = ns_t_mr; return 0; } + if (!strcasecmp(t, "NULL")) { *n = ns_t_null; return 0; } + if (!strcasecmp(t, "WKS")) { *n = ns_t_wks; return 0; } + if (!strcasecmp(t, "PTR")) { *n = ns_t_ptr; return 0; } + if (!strcasecmp(t, "HINFO")) { *n = ns_t_hinfo; return 0; } + if (!strcasecmp(t, "MINFO")) { *n = ns_t_minfo; return 0; } + if (!strcasecmp(t, "TXT")) { *n = ns_t_txt; return 0; } + if (!strcasecmp(t, "RP")) { *n = ns_t_rp; return 0; } + if (!strcasecmp(t, "AFSDB")) { *n = ns_t_afsdb; return 0; } + if (!strcasecmp(t, "X25")) { *n = ns_t_x25; return 0; } + if (!strcasecmp(t, "ISDN")) { *n = ns_t_isdn; return 0; } + if (!strcasecmp(t, "RT")) { *n = ns_t_rt; return 0; } + if (!strcasecmp(t, "NSAP")) { *n = ns_t_nsap; return 0; } + if (!strcasecmp(t, "NSPTR")) { *n = ns_t_nsap_ptr; return 0; } + if (!strcasecmp(t, "NSAP_PTR")){ *n = ns_t_nsap_ptr; return 0; } + if (!strcasecmp(t, "SIG")) { *n = ns_t_sig; return 0; } + if (!strcasecmp(t, "KEY")) { *n = ns_t_key; return 0; } + if (!strcasecmp(t, "PX")) { *n = ns_t_px; return 0; } + if (!strcasecmp(t, "GPOS")) { *n = ns_t_gpos; return 0; } + if (!strcasecmp(t, "AAAA")) { *n = ns_t_aaaa; return 0; } + if (!strcasecmp(t, "LOC")) { *n = ns_t_loc; return 0; } + if (!strcasecmp(t, "NXT")) { *n = ns_t_nxt; return 0; } + if (!strcasecmp(t, "EID")) { *n = ns_t_eid; return 0; } + if (!strcasecmp(t, "NIMLOC")) { *n = ns_t_nimloc; return 0; } + if (!strcasecmp(t, "SRV")) { *n = ns_t_srv; return 0; } + if (!strcasecmp(t, "ATMA")) { *n = ns_t_atma; return 0; } + if (!strcasecmp(t, "NAPTR")) { *n = ns_t_naptr; return 0; } + if (!strcasecmp(t, "KX")) { *n = ns_t_kx; return 0; } + if (!strcasecmp(t, "CERT")) { *n = ns_t_cert; return 0; } + if (!strcasecmp(t, "A6")) { *n = ns_t_a6; return 0; } + if (!strcasecmp(t, "DNAME")) { *n = ns_t_dname; return 0; } + if (!strcasecmp(t, "SINK")) { *n = ns_t_sink; return 0; } + if (!strcasecmp(t, "OPT")) { *n = ns_t_opt; return 0; } + if (!strcasecmp(t, "TKEY")) { *n = ns_t_tkey; return 0; } + if (!strcasecmp(t, "TSIG")) { *n = ns_t_tsig; return 0; } + if (!strcasecmp(t, "IXFR")) { *n = ns_t_ixfr; return 0; } + if (!strcasecmp(t, "AXFR")) { *n = ns_t_axfr; return 0; } + if (!strcasecmp(t, "MAILB")) { *n = ns_t_mailb; return 0; } + if (!strcasecmp(t, "MAILA")) { *n = ns_t_maila; return 0; } + if (!strcasecmp(t, "ANY")) { *n = ns_t_any; return 0; } + if (!strcasecmp(t, "ZXFR")) { *n = ns_t_zxfr; return 0; } + + return -1; +} + +const char * +dns_class_string(uint16_t c) +{ + switch (c) + { + case ns_c_in: return "IN"; + case ns_c_2: return "CS"; + case ns_c_chaos: return "CH"; + case ns_c_hs: return "HS"; + case ns_c_none: return "NONE"; + case ns_c_any: return "ANY"; + default: return "??"; + } + + return "??"; +} + +int32_t +dns_class_number(const char *c, uint16_t *n) +{ + if (c == NULL) return -1; + + if (!strcasecmp(c, "IN")) { *n = ns_c_in; return 0; } + if (!strcasecmp(c, "CS")) { *n = ns_c_2; return 0; } + if (!strcasecmp(c, "CH")) { *n = ns_c_chaos; return 0; } + if (!strcasecmp(c, "HS")) { *n = ns_c_hs; return 0; } + if (!strcasecmp(c, "NONE")) { *n = ns_c_none; return 0; } + if (!strcasecmp(c, "ANY")) { *n = ns_c_any; return 0; } + + return -1; +} + +static void +_dns_print_question_lock(const dns_question_t *q, FILE *f, int lockit) +{ + if (lockit != 0) _dns_print_lock(); + fprintf(f, "%s %s %s\n", q->name, dns_class_string(q->dnsclass), dns_type_string(q->dnstype)); + if (lockit != 0) _dns_print_unlock(); +} + +void +dns_print_question(const dns_question_t *q, FILE *f) +{ + _dns_print_question_lock(q, f, 1); +} + +static void +_dns_print_resource_record_lock(const dns_resource_record_t *r, FILE *f, int lockit) +{ + struct protoent *p; + struct servent *s; + uint32_t i, len; + uint8_t x; + struct sockaddr_in6 s6; + char pbuf[64]; + + if (lockit != 0) _dns_print_lock(); + + fprintf(f, "%s %s %s ", r->name, dns_class_string(r->dnsclass), dns_type_string(r->dnstype)); + fprintf(f, "%u", r->ttl); + + switch (r->dnstype) + { + case ns_t_a: + fprintf(f, " %s", inet_ntoa(r->data.A->addr)); + break; + + case ns_t_aaaa: + memset(&s6, 0, sizeof(struct sockaddr_in6)); + s6.sin6_len = sizeof(struct sockaddr_in6); + s6.sin6_family = AF_INET6; + s6.sin6_addr = r->data.AAAA->addr; + fprintf(f, " %s", inet_ntop(AF_INET6, (char *)(&s6) + INET_NTOP_AF_INET6_OFFSET, pbuf, 64)); + break; + + case ns_t_md: + case ns_t_mf: + case ns_t_cname: + case ns_t_mb: + case ns_t_mg: + case ns_t_mr: + case ns_t_ptr: + case ns_t_ns: + fprintf(f, " %s", r->data.CNAME->name); + break; + + case ns_t_soa: + fprintf(f, " %s %s %u %u %u %u %u", + r->data.SOA->mname, r->data.SOA->rname, + r->data.SOA->serial, r->data.SOA->refresh, r->data.SOA->retry, + r->data.SOA->expire, r->data.SOA->minimum); + break; + + case ns_t_wks: + fprintf(f, " %s", inet_ntoa(r->data.WKS->addr)); + p = getprotobynumber(r->data.WKS->protocol); + if (p != NULL) + { + fprintf(f, " %s", p->p_name); + + for (i = 0; i < r->data.WKS->maplength; i++) + { + if (r->data.WKS->map[i]) + { + s = getservbyport(i, p->p_name); + if (s == NULL) fprintf(f, " %u", i); + else fprintf(f, " %s", s->s_name); + } + } + } + else fprintf(f, " UNKNOWN PROTOCOL %u", r->data.WKS->protocol); + break; + + case ns_t_hinfo: + fprintf(f, " %s %s", r->data.HINFO->cpu, r->data.HINFO->os); + break; + + case ns_t_minfo: + fprintf(f, " %s %s", r->data.MINFO->rmailbx, r->data.MINFO->emailbx); + break; + + case ns_t_mx: + fprintf(f, " %u %s", r->data.MX->preference, r->data.MX->name); + break; + + case ns_t_txt: + for (i = 0; i < r->data.TXT->string_count; i++) + { + fprintf(f, " \"%s\"", r->data.TXT->strings[i]); + } + break; + + case ns_t_rp: + fprintf(f, " %s %s", r->data.RP->mailbox, r->data.RP->txtdname); + break; + + case ns_t_afsdb: + fprintf(f, " %u %s", r->data.AFSDB->subtype, + r->data.AFSDB->hostname); + break; + + case ns_t_x25: + fprintf(f, " %s", r->data.X25->psdn_address); + break; + + case ns_t_isdn: + fprintf(f, " %s", r->data.ISDN->isdn_address); + if (r->data.ISDN->subaddress != NULL) + fprintf(f, " %s", r->data.ISDN->subaddress); + break; + + case ns_t_rt: + fprintf(f, " %hu %s", r->data.RT->preference, + r->data.RT->intermediate); + break; + + case ns_t_loc: + fprintf(f, " %s", coord_ntoa(r->data.LOC->latitude, 1)); + fprintf(f, " %s", coord_ntoa(r->data.LOC->longitude, 0)); + fprintf(f, " %sm", alt_ntoa(r->data.LOC->altitude)); + fprintf(f, " %sm", precsize_ntoa(r->data.LOC->size)); + fprintf(f, " %sm", precsize_ntoa(r->data.LOC->horizontal_precision)); + fprintf(f, " %sm", precsize_ntoa(r->data.LOC->vertical_precision)); + break; + + case ns_t_srv: + fprintf(f, " %hu %hu %hu %s", + r->data.SRV->priority, r->data.SRV->weight, + r->data.SRV->port, r->data.SRV->target); + break; + + case ns_t_null: + default: + len = r->data.DNSNULL->length; + fprintf(f, " %hu ", len); + for (i = 0; i < len; i++) + { + x = r->data.DNSNULL->data[i]; + fprintf(f, "%x", x); + } + + fprintf(f, " ("); + + len = r->data.DNSNULL->length; + for (i = 0; i < len; i++) + { + x = r->data.DNSNULL->data[i]; + if (isascii(x)) fprintf(f, "%c", x); + else fprintf(f, " "); + } + fprintf(f, ")\n"); + + } + + fprintf(f, "\n"); + + if (lockit != 0) _dns_print_unlock(); +} + +void +dns_print_resource_record(const dns_resource_record_t *r, FILE *f) +{ + _dns_print_resource_record_lock(r, f, 1); +} + +void +dns_print_reply(const dns_reply_t *r, FILE *f, uint16_t mask) +{ + uint16_t i; + dns_header_t *h; + char scratch[1024]; + uint32_t offset, iface; + + _dns_print_lock(); + + if (r == NULL) + { + fprintf(f, "-nil-\n"); + _dns_print_unlock(); + return; + } + + if (r->status != DNS_STATUS_OK) + { + if (r->status == DNS_STATUS_TIMEOUT) + fprintf(f, "Timeout\n"); + else if (r->status == DNS_STATUS_SEND_FAILED) + fprintf(f, "Send failed\n"); + else if (r->status == DNS_STATUS_RECEIVE_FAILED) + fprintf(f, "Receive failed\n"); + else fprintf(f, "status %u\n", r->status); + + _dns_print_unlock(); + return; + } + + h = r->header; + + if (mask & DNS_PRINT_XID) + { + fprintf(f, "Xid: %u\n", h->xid); + } + + if (mask & DNS_PRINT_QR) + { + if ((h->flags & DNS_FLAGS_QR_MASK) == DNS_FLAGS_QR_QUERY) + fprintf(f, "QR: Query\n"); + else + fprintf(f, "QR: Reply\n"); + } + + if (mask & DNS_PRINT_SERVER) + { + if (r->server == NULL) + { + fprintf(f, "Server: -nil-\n"); + } + else + { + offset = INET_NTOP_AF_INET_OFFSET; + if (r->server->sa_family == AF_INET6) offset = INET_NTOP_AF_INET6_OFFSET; + + fprintf(f, "Server: %s", inet_ntop(r->server->sa_family, (char *)(r->server) + offset, scratch, 1024)); + if (r->server->sa_family == AF_INET) + { + memcpy(&iface, (((struct sockaddr_in *)(r->server))->sin_zero), 4); + if (iface > 0) fprintf(f, "%%%s", if_indextoname(iface, scratch)); + } + else if (r->server->sa_family == AF_INET6) + { + iface = ((struct sockaddr_in6 *)(r->server))->sin6_scope_id; + if (iface > 0) fprintf(f, "%%%s", if_indextoname(iface, scratch)); + } + fprintf(f, "\n"); + } + } + + if (mask & DNS_PRINT_OPCODE) + { + fprintf(f, "Opcode: "); + switch (h->flags & DNS_FLAGS_OPCODE_MASK) + { + case ns_o_query: fprintf(f, "Standard\n"); break; + case ns_o_iquery: fprintf(f, "Inverse\n"); break; + case ns_o_status: fprintf(f, "Status\n"); break; + case ns_o_notify: fprintf(f, "Notify\n"); break; + case ns_o_update: fprintf(f, "Update\n"); break; + default: + fprintf(f, "Reserved (%hu)\n", + (h->flags & DNS_FLAGS_OPCODE_MASK) >> 11); + } + } + + if (mask & DNS_PRINT_AA) + { + if (h->flags & DNS_FLAGS_AA) fprintf(f, "AA: Authoritative\n"); + else fprintf(f, "AA: Non-Authoritative\n"); + } + + if (mask & DNS_PRINT_TC) + { + if (h->flags & DNS_FLAGS_TC) fprintf(f, "TC: Truncated\n"); + else fprintf(f, "TC: Non-Truncated\n"); + } + + if (mask & DNS_PRINT_RD) + { + if (h->flags & DNS_FLAGS_RD) fprintf(f, "RD: Recursion desired\n"); + else fprintf(f, "RD: No recursion desired\n"); + } + + if (mask & DNS_PRINT_RA) + { + if (h->flags & DNS_FLAGS_RA) fprintf(f, "RA: Recursion available\n"); + else fprintf(f, "RA: No recursion available \n"); + } + + if (mask & DNS_PRINT_RCODE) + { + fprintf(f, "Rcode: "); + switch (h->flags & DNS_FLAGS_RCODE_MASK) + { + case ns_r_noerror: + fprintf(f, "No error\n"); + break; + case ns_r_formerr: + fprintf(f, "Format error \n"); + break; + case ns_r_servfail: + fprintf(f, "Server failure\n"); + break; + case ns_r_nxdomain: + fprintf(f, "Name error \n"); + break; + case ns_r_notimpl: + fprintf(f, "Not implemented\n"); + break; + case ns_r_refused: + fprintf(f, "Refused\n"); + break; + case ns_r_yxdomain: + fprintf(f, "Name exists\n"); + break; + case ns_r_yxrrset: + fprintf(f, "RR Set exists\n"); + break; + case ns_r_nxrrset: + fprintf(f, "RR Set does not exist\n"); + break; + case ns_r_notauth: + fprintf(f, "Not authoritative\n"); + break; + case ns_r_notzone: + fprintf(f, "Record zone does not match section zone\n"); + break; + case ns_r_badvers: + fprintf(f, "Invalid EDNS version or TSIG signature\n"); + break; + case ns_r_badkey: + fprintf(f, "Invalid key\n"); + break; + case ns_r_badtime: + fprintf(f, "Invalid time\n"); + break; + default: + fprintf(f, "Reserved (%hu)\n",h->flags & DNS_FLAGS_RCODE_MASK); + } + } + + if (mask & DNS_PRINT_QUESTION) + { + fprintf(f, "Question (%hu):\n", h->qdcount); + for (i = 0; i < h->qdcount; i++) + _dns_print_question_lock(r->question[i], f, 0); + } + + if (mask & DNS_PRINT_ANSWER) + { + fprintf(f, "Answer (%hu):\n", h->ancount); + for (i = 0; i < h->ancount; i++) + _dns_print_resource_record_lock(r->answer[i], f, 0); + } + + if (mask & DNS_PRINT_AUTHORITY) + { + fprintf(f, "Authority (%hu):\n", h->nscount); + for (i = 0; i < h->nscount; i++) + _dns_print_resource_record_lock(r->authority[i], f, 0); + } + + if (mask & DNS_PRINT_ADDITIONAL) + { + fprintf(f, "Additional records (%hu):\n", h->arcount); + for (i = 0; i < h->arcount; i++) + _dns_print_resource_record_lock(r->additional[i], f, 0); + } + + _dns_print_unlock(); +} + +static void +_pdns_print_handle(pdns_handle_t *pdns, FILE *f) +{ + uint32_t i, offset; + struct in_addr a; + char scratch[1024]; + struct sockaddr *sa; + res_state r; + + if (pdns == NULL) + { + fprintf(f, "-nil-\n"); + return; + } + + if (pdns->name == NULL) fprintf(f, "Name: -nil-\n"); + else fprintf(f, "Name: %s\n", pdns->name); + + fprintf(f, "Flags:"); + if (pdns->flags == 0) fprintf(f, " None\n"); + else + { + if (pdns->flags & DNS_FLAG_DEBUG) fprintf(f, " Debug"); + if (pdns->flags & DNS_FLAG_CHECK_RESOLVER_DIR) fprintf(f, " DirCheck"); + if (pdns->flags & DNS_FLAG_HAVE_IPV6_SERVER) fprintf(f, " IPv6"); + if (pdns->flags & DNS_FLAG_OK_TO_SKIP_AAAA) fprintf(f, " SkipAAAA"); + if (pdns->flags & DNS_FLAG_DEFAULT_RESOLVER) fprintf(f, " Default"); + fprintf(f, "\n"); + } + + r = pdns->res; + if (r == NULL) return; + + if (r->defdname[0] != '\0') fprintf(f, "Domain: %s\n", r->defdname); + fprintf(f, "Search Order: %d\n", pdns->search_order); + fprintf(f, "Total Timeout: %d\n", pdns->total_timeout); + fprintf(f, "Retry Timeout: %d\n", pdns->res->retrans); + fprintf(f, "Retry Attempts: %d\n", pdns->res->retry); + + fprintf(f, "Server%s:\n", (r->nscount == 1) ? "" : "s"); + for (i = 0; i < r->nscount; i++) + { + sa = get_nsaddr(r, i); + offset = INET_NTOP_AF_INET_OFFSET; + if (sa->sa_family == AF_INET6) offset = INET_NTOP_AF_INET6_OFFSET; + fprintf(f, " %u: %s", i, inet_ntop(sa->sa_family, (char *)sa + offset, scratch, 1024)); + fprintf(f, "\n"); + } + + if (pdns->search_count > 0) + { + fprintf(f, "Search List:\n"); + for (i = 0; i < pdns->search_count; i++) + fprintf(f, " %u: %s\n", i, pdns->search_list[i]); + } + + if (r->sort_list[0].addr.s_addr != 0) + { + fprintf(f, "Sortlist:\n"); + for (i = 0; (r->sort_list[i].addr.s_addr != 0); i++) + { + fprintf(f, " %u: ", i); + a.s_addr = r->sort_list[i].addr.s_addr; + fprintf(f, "%s/", inet_ntoa(a)); + a.s_addr = r->sort_list[i].mask; + fprintf(f, "%s\n", inet_ntoa(a)); + } + } +} + +static void +_sdns_print_handle(sdns_handle_t *sdns, FILE *f) +{ + int i; + + if (sdns == NULL) + { + fprintf(f, "-nil-\n"); + return; + } + + for (i = 0; i < sdns->client_count; i++) + { + fprintf(f, "DNS client %d\n", i); + _pdns_print_handle(sdns->client[i], f); + fprintf(f, "\n"); + } + + fprintf(f, "resolver dir mod time = %u\n", sdns->modtime); + fprintf(f, "resolver dir stat time = %u\n", sdns->stattime); + fprintf(f, "resolver dir stat latency = %u\n", sdns->stat_latency); +} + +void +dns_print_handle(dns_handle_t d, FILE *f) +{ + dns_private_handle_t *dns; + + _dns_print_lock(); + + if (d == NULL) + { + fprintf(f, "-nil-\n"); + _dns_print_unlock(); + return; + } + + dns = (dns_private_handle_t *)d; + + if (dns->handle_type == DNS_PRIVATE_HANDLE_TYPE_SUPER) + { + _sdns_print_handle(dns->sdns, f); + } + else + { + _pdns_print_handle(dns->pdns, f); + } + + _dns_print_unlock(); +} + +void +dns_all_server_addrs(dns_handle_t d, struct sockaddr ***addrs, uint32_t *count) +{ + int i, j, k, n, found; + dns_private_handle_t *dns; + pdns_handle_t *pdns; + struct sockaddr *sa; + struct sockaddr_storage **l; + res_state r; + + *addrs = NULL; + *count = 0; + l = NULL; + n = 0; + + if (d == NULL) return; + + dns = (dns_private_handle_t *)d; + + if (dns->handle_type != DNS_PRIVATE_HANDLE_TYPE_SUPER) return; + + if (dns->sdns == NULL) return; + + /* Just to initialize / validate clients */ + i = dns_search_list_count(d); + + for (i = 0; i < dns->sdns->client_count; i++) + { + pdns = dns->sdns->client[i]; + if (pdns == NULL) continue; + + r = pdns->res; + if (r == NULL) continue; + + for (j = 0; j < r->nscount; j++) + { + sa = get_nsaddr(r, j); + found = 0; + for (k = 0; (found == 0) && (k < n); k++) + { + if (memcmp(l[k], sa, sa->sa_len) == 0) found = 1; + } + if (found == 1) continue; + + if (n == 0) + { + l = (struct sockaddr_storage **)calloc(1, sizeof(struct sockaddr_storage *)); + } + else + { + l = (struct sockaddr_storage **)reallocf(l, (n + 1) * sizeof(struct sockaddr_storage *)); + } + + if (l == NULL) return; + + l[n] = (struct sockaddr_storage *)calloc(1, sizeof(struct sockaddr_storage)); + if (l[n] == NULL) return; + + memset(l[n], 0, sizeof(struct sockaddr_storage)); + memcpy(l[n], sa, sa->sa_len); + n++; + } + } + + *addrs = (struct sockaddr **)l; + *count = n; +} + +int +dns_res_once(struct sockaddr *server, struct timeval *timeout, int options, const char *name, int class, int type, u_char *res, int *reslen) +{ + res_state statp; + int n, fromlen, status; + struct sockaddr_storage from; + u_char buf[MAXPACKET]; + + if (server == NULL) return DNS_RES_STATUS_INVALID_ARGUMENT; + if (name == NULL) return DNS_RES_STATUS_INVALID_ARGUMENT; + if (res == NULL) return DNS_RES_STATUS_INVALID_ARGUMENT; + if (reslen == NULL) return DNS_RES_STATUS_INVALID_ARGUMENT; + + fromlen = sizeof(struct sockaddr_storage); + + statp = res_state_new(); + statp->retry = 1; + statp->options = options; + statp->id = res_randomid(); + if (timeout == NULL) statp->retrans = 5; + else statp->retrans = timeout->tv_sec; + + statp->ndots = 1; + statp->_vcsock = -1; + statp->nscount = 1; + + strcpy(statp->_u._ext.ext->nsuffix, "ip6.arpa"); + strcpy(statp->_u._ext.ext->nsuffix2, "ip6.int"); + strcpy(statp->_u._ext.ext->bsuffix, "ip6.arpa"); + + if (server->sa_family == AF_INET6) + { + memcpy(&(statp->_u._ext.ext->nsaddrs[0]), server, sizeof(struct sockaddr_in6)); + statp->nsaddr_list[0].sin_family = 0; + } + else + { + memcpy(&(statp->_u._ext.ext->nsaddrs[0]), server, sizeof(struct sockaddr_in)); + memcpy(&(statp->nsaddr_list[0]), server, sizeof(struct sockaddr_in)); + } + + n = res_nmkquery(statp, ns_o_query, name, class, type, NULL, 0, NULL, buf, sizeof(buf)); + + status = dns_res_send(statp, buf, n, res, reslen, (struct sockaddr *)&from, &fromlen); + + res_client_close(statp); + + return status; +} diff --git a/dns_util.h b/dns_util.h new file mode 100644 index 0000000..a291584 --- /dev/null +++ b/dns_util.h @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * "Portions Copyright (c) 2003 Apple Computer, Inc. All Rights + * Reserved. This file contains Original Code and/or Modifications of + * Original Code as defined in and that are subject to the Apple Public + * Source License Version 1.0 (the 'License'). You may not use this file + * except in compliance with the License. Please obtain a copy of the + * License at http://www.apple.com/publicsource and read it before using + * this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License." + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __DNS_UTIL_H__ +#define __DNS_UTIL_H__ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Status returned in a dns_reply_t + */ +#define DNS_STATUS_OK 0 +#define DNS_STATUS_BAD_HANDLE 1 +#define DNS_STATUS_MALFORMED_QUERY 2 +#define DNS_STATUS_TIMEOUT 3 +#define DNS_STATUS_SEND_FAILED 4 +#define DNS_STATUS_RECEIVE_FAILED 5 +#define DNS_STATUS_CONNECTION_FAILED 6 +#define DNS_STATUS_WRONG_SERVER 7 +#define DNS_STATUS_WRONG_XID 8 +#define DNS_STATUS_WRONG_QUESTION 9 + +/* + * dns_print_reply mask + */ +#define DNS_PRINT_XID 0x0001 +#define DNS_PRINT_QR 0x0002 +#define DNS_PRINT_OPCODE 0x0004 +#define DNS_PRINT_AA 0x0008 +#define DNS_PRINT_TC 0x0010 +#define DNS_PRINT_RD 0x0020 +#define DNS_PRINT_RA 0x0040 +#define DNS_PRINT_PR 0x0080 +#define DNS_PRINT_RCODE 0x0100 +#define DNS_PRINT_QUESTION 0x0200 +#define DNS_PRINT_ANSWER 0x0400 +#define DNS_PRINT_AUTHORITY 0x0800 +#define DNS_PRINT_ADDITIONAL 0x1000 +#define DNS_PRINT_SERVER 0x2000 + +/* + * DNS query / reply header + */ +typedef struct { + uint16_t xid; + uint16_t flags; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +} dns_header_t; + +/* + * DNS query + */ +typedef struct +{ + char *name; + uint16_t dnstype; + uint16_t dnsclass; +} dns_question_t; + +/* + * Resource Record types + * dns_parse_packet() creates resourse records of these types. + */ +typedef struct +{ + uint16_t length; + char *data; +} dns_raw_resource_record_t; + +typedef struct +{ + struct in_addr addr; +} dns_address_record_t; + +typedef struct +{ + struct in6_addr addr; +} dns_in6_address_record_t; + +typedef struct +{ + char *name; +} dns_domain_name_record_t; + +typedef struct +{ + char *mname; + char *rname; + uint32_t serial; + uint32_t refresh; + uint32_t retry; + uint32_t expire; + uint32_t minimum; +} dns_SOA_record_t; + +typedef struct +{ + char *cpu; + char *os; +} dns_HINFO_record_t; + +typedef struct +{ + char *rmailbx; + char *emailbx; +} dns_MINFO_record_t; + +typedef struct +{ + uint16_t preference; + char *name; +} dns_MX_record_t; + +typedef struct +{ + uint32_t string_count; + char **strings; +} dns_TXT_record_t; + +typedef struct +{ + struct in_addr addr; + uint8_t protocol; + uint32_t maplength; + uint8_t *map; +} dns_WKS_record_t; + +typedef struct +{ + char *mailbox; + char *txtdname; +} dns_RP_record_t; + +typedef struct +{ + uint32_t subtype; + char *hostname; +} dns_AFSDB_record_t; + +typedef struct +{ + char *psdn_address; +} dns_X25_record_t; + +typedef struct +{ + char *isdn_address; + char *subaddress; +} dns_ISDN_record_t; + +typedef struct +{ + uint16_t preference; + char * intermediate; +} dns_RT_record_t; + +typedef struct +{ + uint8_t version; + uint8_t size; + uint8_t horizontal_precision; + uint8_t vertical_precision; + uint32_t latitude; + uint32_t longitude; + uint32_t altitude; +} dns_LOC_record_t; + +typedef struct +{ + uint16_t priority; + uint16_t weight; + uint16_t port; + char *target; +} dns_SRV_record_t; + +/* + * DNS Resource Record + * + * Data contained in unsupported or obsolete Resource Record types + * may be accessed via DNSNULL as a dns_raw_resource_record_t. + */ +typedef struct +{ + char *name; + uint16_t dnstype; + uint16_t dnsclass; + uint32_t ttl; + union + { + dns_address_record_t *A; + dns_domain_name_record_t *NS; + dns_domain_name_record_t *MD; /* Obsolete */ + dns_domain_name_record_t *MF; /* Obsolete */ + dns_domain_name_record_t *CNAME; + dns_SOA_record_t *SOA; + dns_domain_name_record_t *MB; + dns_domain_name_record_t *MG; + dns_domain_name_record_t *MR; + dns_raw_resource_record_t *DNSNULL; + dns_WKS_record_t *WKS; + dns_domain_name_record_t *PTR; + dns_HINFO_record_t *HINFO; + dns_MINFO_record_t *MINFO; + dns_MX_record_t *MX; + dns_TXT_record_t *TXT; + dns_RP_record_t *RP; + dns_AFSDB_record_t *AFSDB; + dns_X25_record_t *X25; + dns_ISDN_record_t *ISDN; + dns_RT_record_t *RT; + dns_in6_address_record_t *AAAA; + dns_LOC_record_t *LOC; + dns_SRV_record_t *SRV; + } data; +} dns_resource_record_t; + +/* + * A parsed DNS record. Returned by dns_parse_packet() and dns_lookup(). + * The contents may be printed using dns_print_reply(). + */ +typedef struct +{ + uint32_t status; + struct sockaddr *server; + dns_header_t *header; + dns_question_t **question; + dns_resource_record_t **answer; + dns_resource_record_t **authority; + dns_resource_record_t **additional; +} dns_reply_t; + + +__BEGIN_DECLS + +/* + * High-level lookup performs a search (using dns_search), parses the + * reply and returns a dns_reply_t structure. + * + * The DNS handle contains an internal buffer used for fetching replies. + * The buffer is reused for each query, and is released with the DNS client + * handle when dns_free() is called. The default buffer size is 1024 bytes. + * The size may be changed with dns_set_buffer_size. + * + * Note that in a multithreaded application, each thread using this API must + * open a separate handle. + */ +extern dns_reply_t *dns_lookup(dns_handle_t dns, const char *name, uint32_t dnsclass, uint32_t dnstype); + +/* + * Get / Set the size of the internal receive buffer used by dns_lookup() + */ +extern uint32_t dns_get_buffer_size(dns_handle_t d); +extern void dns_set_buffer_size(dns_handle_t d, uint32_t len); + +/* + * Parse a reply packet into a reply structure. + */ +extern dns_reply_t *dns_parse_packet(const char *buf, uint32_t len); + +/* + * Free a reply structure. + */ +extern void dns_free_reply(dns_reply_t *r); + +/* + * Parse a query packet into a question structure. + */ +extern dns_question_t *dns_parse_question(const char *buf, uint32_t len); + +/* + * Free a question structure. + */ +extern void dns_free_question(dns_question_t *q); + +/* + * Parse a resource record into a structure. + */ +extern dns_resource_record_t *dns_parse_resource_record(const char *buf, uint32_t len); + +/* + * Free a resource record structure. + */ +extern void dns_free_resource_record(dns_resource_record_t *rr); + +/* + * String / number representation of a DNS class + * dns_class_number returns 0 if the string is recognized, + * non-zero if the class string is unknown. + */ +extern const char *dns_class_string(uint16_t dnsclass); +extern int32_t dns_class_number(const char *c, uint16_t *n); + +/* + * String / number representation of a DNS type + * dns_type_number returns 0 if the string is recognized, + * non-zero if the class string is unknown. + */ +extern const char *dns_type_string(uint16_t dnstype); +extern int32_t dns_type_number(const char *t, uint16_t *n); + +/* + * Print a dns handle. + */ +extern void dns_print_handle(dns_handle_t d, FILE *f); + +/* + * Print the contents of a question structure. + */ +extern void dns_print_question(const dns_question_t *q, FILE *f); + +/* + * Print the contents of a resource record structure. + */ +extern void dns_print_resource_record(const dns_resource_record_t *r, FILE *f); + +/* + * Print the contents of a reply structure. + */ +extern void dns_print_reply(const dns_reply_t *r, FILE *f, uint16_t mask); + +__END_DECLS + +#endif /* __DNS_UTIL_H__ */ diff --git a/dst.h b/dst.h new file mode 100644 index 0000000..791bd82 --- /dev/null +++ b/dst.h @@ -0,0 +1,156 @@ +#ifndef DST_H +#define DST_H + +#ifndef HAS_DST_KEY +#define DST_KEY RES_9_DST_KEY +typedef struct dst_key { + char *dk_key_name; /* name of the key */ + int dk_key_size; /* this is the size of the key in bits */ + int dk_proto; /* what protocols this key can be used for */ + int dk_alg; /* algorithm number from key record */ + u_int32_t dk_flags; /* and the flags of the public key */ + u_int16_t dk_id; /* identifier of the key */ +} DST_KEY; +#endif /* HAS_DST_KEY */ + +/* + * DST Crypto API defintions + */ +#define dst_init res_9_dst_init +void dst_init(void); +#ifndef __APPLE__ +int dst_check_algorithm(const int); +#endif + +#define dst_sign_data res_9_dst_sign_data +int dst_sign_data(const int mode, /* specifies INIT/UPDATE/FINAL/ALL */ + DST_KEY *in_key, /* the key to use */ + void **context, /* pointer to state structure */ + const u_char *data, /* data to be signed */ + const int len, /* length of input data */ + u_char *signature, /* buffer to write signature to */ + const int sig_len); /* size of output buffer */ + +#define dst_verify_data res_9_dst_verify_data +int dst_verify_data(const int mode, /* specifies INIT/UPDATE/FINAL/ALL */ + DST_KEY *in_key, /* the key to use */ + void **context, /* pointer to state structure */ + const u_char *data, /* data to be verified */ + const int len, /* length of input data */ + const u_char *signature,/* buffer containing signature */ + const int sig_len); /* length of signature */ + +#define dst_read_key res_9_dst_read_key +DST_KEY *dst_read_key(const char *in_name, /* name of key */ + const u_int16_t in_id, /* key tag identifier */ + const int in_alg, /* key algorithm */ + const int key_type); /* Private/PublicKey wanted*/ + +#define dst_write_key res_9_dst_write_key +int dst_write_key(const DST_KEY *key, /* key to write out */ + const int key_type); /* Public/Private */ + +#define dst_dnskey_to_key res_9_dst_dnskey_to_key +DST_KEY *dst_dnskey_to_key(const char *in_name, /* KEY record name */ + const u_char *key, /* KEY RDATA */ + const int len); /* size of input buffer*/ + +#ifndef __APPLE__ +int dst_key_to_dnskey(const DST_KEY *key, /* key to translate */ + u_char *out_storage, /* output buffer */ + const int out_len); /* size of out_storage*/ +#endif + +#define dst_buffer_to_key res_9_dst_buffer_to_key +DST_KEY *dst_buffer_to_key(const char *key_name, /* name of the key */ + const int alg, /* algorithm */ + const int flags, /* dns flags */ + const int protocol, /* dns protocol */ + const u_char *key_buf, /* key in dns wire fmt */ + const int key_len); /* size of key */ + + +#define dst_key_to_buffer res_9_dst_key_to_buffer +int dst_key_to_buffer(DST_KEY *key, u_char *out_buff, int buf_len); + +#define dst_generate_key res_9_dst_generate_key +DST_KEY *dst_generate_key(const char *name, /* name of new key */ + const int alg, /* key algorithm to generate */ + const int bits, /* size of new key */ + const int exp, /* alg dependent parameter*/ + const int flags, /* key DNS flags */ + const int protocol); /* key DNS protocol */ + +#define dst_free_key res_9_dst_free_key +DST_KEY *dst_free_key(DST_KEY *f_key); + +#define dst_compare_keys res_9_dst_compare_keys +int dst_compare_keys(const DST_KEY *key1, const DST_KEY *key2); + +#define dst_sig_size res_9_dst_sig_size +int dst_sig_size(DST_KEY *key); + +/* support for dns key tags/ids */ +#define dst_s_dns_key_id res_9_dst_s_dns_key_id +u_int16_t dst_s_dns_key_id(const u_char *dns_key_rdata, const int rdata_len); +#ifndef __APPLE__ +u_int16_t dst_s_id_calc(const u_char *key_data, const int key_len); +#endif + +/* Used by callers as well as by the library. */ +#define RAW_KEY_SIZE 8192 /* large enough to store any key */ + +/* DST_API control flags */ +/* These are used used in functions dst_sign_data and dst_verify_data */ +#define SIG_MODE_INIT 1 /* initialize digest */ +#define SIG_MODE_UPDATE 2 /* add data to digest */ +#define SIG_MODE_FINAL 4 /* generate/verify signature */ +#define SIG_MODE_ALL (SIG_MODE_INIT|SIG_MODE_UPDATE|SIG_MODE_FINAL) + +/* Flags for dst_read_private_key() */ +#define DST_FORCE_READ 0x1000000 +#define DST_CAN_SIGN 0x010F +#define DST_NO_AUTHEN 0x8000 +#define DST_EXTEND_FLAG 0x1000 +#define DST_STANDARD 0 +#define DST_PRIVATE 0x2000000 +#define DST_PUBLIC 0x4000000 +#define DST_RAND_SEMI 1 +#define DST_RAND_STD 2 +#define DST_RAND_KEY 3 +#define DST_RAND_DSS 4 + + +/* DST algorithm codes */ +#define KEY_RSA 1 +#define KEY_DH 2 +#define KEY_DSA 3 +#define KEY_PRIVATE 254 +#define KEY_EXPAND 255 +#define KEY_HMAC_MD5 157 +#define KEY_HMAC_SHA1 158 +#define UNKNOWN_KEYALG 0 +#define DST_MAX_ALGS KEY_HMAC_SHA1 + +/* DST constants to locations in KEY record changes in new KEY record */ +#define DST_FLAGS_SIZE 2 +#define DST_KEY_PROT 2 +#define DST_KEY_ALG 3 +#define DST_EXT_FLAG 4 +#define DST_KEY_START 4 + +#ifndef SIGN_F_NOKEY +#define SIGN_F_NOKEY 0xC000 +#endif + +/* error codes from dst routines */ +#define SIGN_INIT_FAILURE (-23) +#define SIGN_UPDATE_FAILURE (-24) +#define SIGN_FINAL_FAILURE (-25) +#define VERIFY_INIT_FAILURE (-26) +#define VERIFY_UPDATE_FAILURE (-27) +#define VERIFY_FINAL_FAILURE (-28) +#define MISSING_KEY_OR_SIGNATURE (-30) +#define UNSUPPORTED_KEYALG (-31) + +#endif /* DST_H */ diff --git a/dst_api.c b/dst_api.c new file mode 100644 index 0000000..2ff3faf --- /dev/null +++ b/dst_api.c @@ -0,0 +1,1076 @@ +#ifndef __APPLE__ +#ifndef LINT +static const char rcsid[] = "$Header: /Users/Shared/libresolv_2/libresolv/dst_api.c,v 1.1 2006/03/01 19:01:36 majka Exp $"; +#endif +#endif + +/* + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * Permission to use, copy modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. + */ +/* + * This file contains the interface between the DST API and the crypto API. + * This is the only file that needs to be changed if the crypto system is + * changed. Exported functions are: + * void dst_init() Initialize the toolkit + * int dst_check_algorithm() Function to determines if alg is suppored. + * int dst_compare_keys() Function to compare two keys for equality. + * int dst_sign_data() Incremental signing routine. + * int dst_verify_data() Incremental verify routine. + * int dst_generate_key() Function to generate new KEY + * DST_KEY *dst_read_key() Function to retrieve private/public KEY. + * void dst_write_key() Function to write out a key. + * DST_KEY *dst_dnskey_to_key() Function to convert DNS KEY RR to a DST + * KEY structure. + * int dst_key_to_dnskey() Function to return a public key in DNS + * format binary + * DST_KEY *dst_buffer_to_key() Converst a data in buffer to KEY + * int *dst_key_to_buffer() Writes out DST_KEY key matterial in buffer + * void dst_free_key() Releases all memory referenced by key structure + */ + +#ifndef __APPLE__ +#include "port_before.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dst_internal.h" +#ifndef __APPLE__ +#include "port_after.h" +#endif + +/* static variables */ +static int done_init = 0; +dst_func *dst_t_func[DST_MAX_ALGS]; +const char *key_file_fmt_str = "Private-key-format: v%s\nAlgorithm: %d (%s)\n"; +const char *dst_path = ""; + +/* internal I/O functions */ +#ifdef _UNUSED_API_ +static DST_KEY *dst_s_read_public_key(const char *in_name, + const u_int16_t in_id, int in_alg); +static int dst_s_read_private_key_file(char *name, DST_KEY *pk_key, + u_int16_t in_id, int in_alg); +static int dst_s_write_public_key(const DST_KEY *key); +static int dst_s_write_private_key(const DST_KEY *key); +#endif + +/* internal function to set up data structure */ +static DST_KEY *dst_s_get_key_struct(const char *name, const int alg, + const int flags, const int protocol, + const int bits); + +/* + * dst_init + * This function initializes the Digital Signature Toolkit. + * Right now, it just checks the DSTKEYPATH environment variable. + * Parameters + * none + * Returns + * none + */ +void +dst_init() +{ + char *s; + int len; + + if (done_init != 0) + return; + done_init = 1; + + s = getenv("DSTKEYPATH"); + len = 0; + if (s) { + struct stat statbuf; + + len = strlen(s); + if (len > PATH_MAX) { + EREPORT(("%s is longer than %d characters, ignoring\n", + s, PATH_MAX)); + } else if (stat(s, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) { + EREPORT(("%s is not a valid directory\n", s)); + } else { + char *tmp; + tmp = (char *) malloc(len + 2); + memcpy(tmp, s, len + 1); + if (tmp[strlen(tmp) - 1] != '/') { + tmp[strlen(tmp) + 1] = 0; + tmp[strlen(tmp)] = '/'; + } + dst_path = tmp; + } + } + memset(dst_t_func, 0, sizeof(dst_t_func)); + /* first one is selected */ + dst_hmac_md5_init(); +} + +/* + * dst_check_algorithm + * This function determines if the crypto system for the specified + * algorithm is present. + * Parameters + * alg 1 KEY_RSA + * 3 KEY_DSA + * 157 KEY_HMAC_MD5 + * future algorithms TBD and registered with IANA. + * Returns + * 1 - The algorithm is available. + * 0 - The algorithm is not available. + */ +#ifdef __APPLE__ +static +#endif +int +dst_check_algorithm(const int alg) +{ + return (dst_t_func[alg] != NULL); +} + +/* + * dst_s_get_key_struct + * This function allocates key structure and fills in some of the + * fields of the structure. + * Parameters: + * name: the name of the key + * alg: the algorithm number + * flags: the dns flags of the key + * protocol: the dns protocol of the key + * bits: the size of the key + * Returns: + * NULL if error + * valid pointer otherwise + */ +static DST_KEY * +dst_s_get_key_struct(const char *name, const int alg, const int flags, + const int protocol, const int bits) +{ + DST_KEY *new_key = NULL; + + if (dst_check_algorithm(alg)) /* make sure alg is available */ + new_key = (DST_KEY *) malloc(sizeof(*new_key)); + if (new_key == NULL) + return (NULL); + + memset(new_key, 0, sizeof(*new_key)); + new_key->dk_key_name = strdup(name); + new_key->dk_alg = alg; + new_key->dk_flags = flags; + new_key->dk_proto = protocol; + new_key->dk_KEY_struct = NULL; + new_key->dk_key_size = bits; + new_key->dk_func = dst_t_func[alg]; + return (new_key); +} + +#ifdef _UNUSED_API_ +/* + * dst_compare_keys + * Compares two keys for equality. + * Parameters + * key1, key2 Two keys to be compared. + * Returns + * 0 The keys are equal. + * non-zero The keys are not equal. + */ + +int +dst_compare_keys(const DST_KEY *key1, const DST_KEY *key2) +{ + if (key1 == key2) + return (0); + if (key1 == NULL || key2 == NULL) + return (4); + if (key1->dk_alg != key2->dk_alg) + return (1); + if (key1->dk_key_size != key2->dk_key_size) + return (2); + if (key1->dk_id != key2->dk_id) + return (3); + return (key1->dk_func->compare(key1, key2)); +} +#endif + +/* + * dst_sign_data + * An incremental signing function. Data is signed in steps. + * First the context must be initialized (SIG_MODE_INIT). + * Then data is hashed (SIG_MODE_UPDATE). Finally the signature + * itself is created (SIG_MODE_FINAL). This function can be called + * once with INIT, UPDATE and FINAL modes all set, or it can be + * called separately with a different mode set for each step. The + * UPDATE step can be repeated. + * Parameters + * mode A bit mask used to specify operation(s) to be performed. + * SIG_MODE_INIT 1 Initialize digest + * SIG_MODE_UPDATE 2 Add data to digest + * SIG_MODE_FINAL 4 Generate signature + * from signature + * SIG_MODE_ALL (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL + * data Data to be signed. + * len The length in bytes of data to be signed. + * in_key Contains a private key to sign with. + * KEY structures should be handled (created, converted, + * compared, stored, freed) by the DST. + * signature + * The location to which the signature will be written. + * sig_len Length of the signature field in bytes. + * Return + * 0 Successfull INIT or Update operation + * >0 success FINAL (sign) operation + * <0 failure + */ + +int +dst_sign_data(const int mode, DST_KEY *in_key, void **context, + const u_char *data, const int len, + u_char *signature, const int sig_len) +{ + DUMP(data, mode, len, "dst_sign_data()"); + + if (mode & SIG_MODE_FINAL && + (in_key->dk_KEY_struct == NULL || signature == NULL)) + return (MISSING_KEY_OR_SIGNATURE); + + if (in_key->dk_func && in_key->dk_func->sign) + return (in_key->dk_func->sign(mode, in_key, context, data, len, + signature, sig_len)); + return (UNKNOWN_KEYALG); +} + + +/* + * dst_verify_data + * An incremental verify function. Data is verified in steps. + * First the context must be initialized (SIG_MODE_INIT). + * Then data is hashed (SIG_MODE_UPDATE). Finally the signature + * is verified (SIG_MODE_FINAL). This function can be called + * once with INIT, UPDATE and FINAL modes all set, or it can be + * called separately with a different mode set for each step. The + * UPDATE step can be repeated. + * Parameters + * mode Operations to perform this time. + * SIG_MODE_INIT 1 Initialize digest + * SIG_MODE_UPDATE 2 add data to digest + * SIG_MODE_FINAL 4 verify signature + * SIG_MODE_ALL + * (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL) + * data Data to pass through the hash function. + * len Length of the data in bytes. + * in_key Key for verification. + * signature Location of signature. + * sig_len Length of the signature in bytes. + * Returns + * 0 Verify success + * Non-Zero Verify Failure + */ + +int +dst_verify_data(const int mode, DST_KEY *in_key, void **context, + const u_char *data, const int len, + const u_char *signature, const int sig_len) +{ + DUMP(data, mode, len, "dst_verify_data()"); + if (mode & SIG_MODE_FINAL && + (in_key->dk_KEY_struct == NULL || signature == NULL)) + return (MISSING_KEY_OR_SIGNATURE); + + if (in_key->dk_func == NULL || in_key->dk_func->verify == NULL) + return (UNSUPPORTED_KEYALG); + return (in_key->dk_func->verify(mode, in_key, context, data, len, + signature, sig_len)); +} + +#ifdef _UNUSED_API_ +/* + * dst_read_private_key + * Access a private key. First the list of private keys that have + * already been read in is searched, then the key accessed on disk. + * If the private key can be found, it is returned. If the key cannot + * be found, a null pointer is returned. The options specify required + * key characteristics. If the private key requested does not have + * these characteristics, it will not be read. + * Parameters + * in_keyname The private key name. + * in_id The id of the private key. + * options DST_FORCE_READ Read from disk - don't use a previously + * read key. + * DST_CAN_SIGN The key must be useable for signing. + * DST_NO_AUTHEN The key must be useable for authentication. + * DST_STANDARD Return any key + * Returns + * NULL If there is no key found in the current directory or + * this key has not been loaded before. + * !NULL Success - KEY structure returned. + */ + +DST_KEY * +dst_read_key(const char *in_keyname, const u_int16_t in_id, + const int in_alg, const int type) +{ + char keyname[PATH_MAX]; + DST_KEY *dg_key = NULL, *pubkey = NULL; + + if (!dst_check_algorithm(in_alg)) { /* make sure alg is available */ + EREPORT(("dst_read_private_key(): Algorithm %d not suppored\n", + in_alg)); + return (NULL); + } + if ((type & (DST_PUBLIC | DST_PRIVATE)) == 0) + return (NULL); + if (in_keyname == NULL) { + EREPORT(("dst_read_private_key(): Null key name passed in\n")); + return (NULL); + } else + strcpy(keyname, in_keyname); + + /* before I read in the public key, check if it is allowed to sign */ + if ((pubkey = dst_s_read_public_key(keyname, in_id, in_alg)) == NULL) + return (NULL); + + if (type == DST_PUBLIC) + return pubkey; + + if (!(dg_key = dst_s_get_key_struct(keyname, pubkey->dk_alg, + pubkey->dk_flags, pubkey->dk_proto, + 0))) + return (dg_key); + /* Fill in private key and some fields in the general key structure */ + if (dst_s_read_private_key_file(keyname, dg_key, pubkey->dk_id, + pubkey->dk_alg) == 0) + dg_key = dst_free_key(dg_key); + + pubkey = dst_free_key(pubkey); + return (dg_key); +} +#endif + +#ifdef _UNUSED_API_ +int +dst_write_key(const DST_KEY *key, const int type) +{ + int pub = 0, priv = 0; + + if (key == NULL) + return (0); + if (!dst_check_algorithm(key->dk_alg)) { /* make sure alg is available */ + EREPORT(("dst_write_key(): Algorithm %d not suppored\n", + key->dk_alg)); + return (UNSUPPORTED_KEYALG); + } + if ((type & (DST_PRIVATE|DST_PUBLIC)) == 0) + return (0); + + if (type & DST_PUBLIC) + if ((pub = dst_s_write_public_key(key)) < 0) + return (pub); + if (type & DST_PRIVATE) + if ((priv = dst_s_write_private_key(key)) < 0) + return (priv); + return (priv+pub); +} +#endif + +#ifdef _UNUSED_API_ +/* + * dst_write_private_key + * Write a private key to disk. The filename will be of the form: + * Kdk_name>+dk_alg>+dk_id>.. + * If there is already a file with this name, an error is returned. + * + * Parameters + * key A DST managed key structure that contains + * all information needed about a key. + * Return + * >= 0 Correct behavior. Returns length of encoded key value + * written to disk. + * < 0 error. + */ + +static int +dst_s_write_private_key(const DST_KEY *key) +{ + u_char encoded_block[RAW_KEY_SIZE]; + char file[PATH_MAX]; + int len; + FILE *fp; + + /* First encode the key into the portable key format */ + if (key == NULL) + return (-1); + if (key->dk_KEY_struct == NULL) + return (0); /* null key has no private key */ + + if (key->dk_func == NULL || key->dk_func->to_file_fmt == NULL) { + EREPORT(("dst_write_private_key(): Unsupported operation %d\n", + key->dk_alg)); + return (-5); + } else if ((len = key->dk_func->to_file_fmt(key, (char *)encoded_block, + sizeof(encoded_block))) <= 0) { + EREPORT(("dst_write_private_key(): Failed encoding private RSA bsafe key %d\n", len)); + return (-8); + } + /* Now I can create the file I want to use */ + dst_s_build_filename(file, key->dk_key_name, key->dk_id, key->dk_alg, + PRIVATE_KEY, PATH_MAX); + + /* Do not overwrite an existing file */ + if ((fp = dst_s_fopen(file, "w", 0600)) != NULL) { + int nn; + if ((nn = fwrite(encoded_block, 1, len, fp)) != len) { + EREPORT(("dst_write_private_key(): Write failure on %s %d != %d errno=%d\n", + file, len, nn, errno)); + return (-5); + } + fclose(fp); + } else { + EREPORT(("dst_write_private_key(): Can not create file %s\n" + ,file)); + return (-6); + } + memset(encoded_block, 0, len); + return (len); +} +#endif + +#ifdef _UNUSED_API_ +/* +* + * dst_read_public_key + * Read a public key from disk and store in a DST key structure. + * Parameters + * in_name K. is the + * filename of the key file to be read. + * Returns + * NULL If the key does not exist or no name is supplied. + * NON-NULL Initialized key structure if the key exists. + */ + +static DST_KEY * +dst_s_read_public_key(const char *in_name, const u_int16_t in_id, int in_alg) +{ + int flags, proto, alg, len, dlen; + int c; + char name[PATH_MAX], enckey[RAW_KEY_SIZE], *notspace; + u_char deckey[RAW_KEY_SIZE]; + FILE *fp; + + if (in_name == NULL) { + EREPORT(("dst_read_public_key(): No key name given\n")); + return (NULL); + } + if (dst_s_build_filename(name, in_name, in_id, in_alg, PUBLIC_KEY, + PATH_MAX) == -1) { + EREPORT(("dst_read_public_key(): Cannot make filename from %s, %d, and %s\n", + in_name, in_id, PUBLIC_KEY)); + return (NULL); + } + /* + * Open the file and read it's formatted contents up to key + * File format: + * domain.name [ttl] [IN] KEY + * flags, proto, alg stored as decimal (or hex numbers FIXME). + * (FIXME: handle parentheses for line continuation.) + */ + if ((fp = dst_s_fopen(name, "r", 0)) == NULL) { + EREPORT(("dst_read_public_key(): Public Key not found %s\n", + name)); + return (NULL); + } + /* Skip domain name, which ends at first blank */ + while ((c = getc(fp)) != EOF) + if (isspace(c)) + break; + /* Skip blank to get to next field */ + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + + /* Skip optional TTL -- if initial digit, skip whole word. */ + if (isdigit(c)) { + while ((c = getc(fp)) != EOF) + if (isspace(c)) + break; + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + } + /* Skip optional "IN" */ + if (c == 'I' || c == 'i') { + while ((c = getc(fp)) != EOF) + if (isspace(c)) + break; + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + } + /* Locate and skip "KEY" */ + if (c != 'K' && c != 'k') { + EREPORT(("\"KEY\" doesn't appear in file: %s", name)); + return NULL; + } + while ((c = getc(fp)) != EOF) + if (isspace(c)) + break; + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + ungetc(c, fp); /* return the charcter to the input field */ + /* Handle hex!! FIXME. */ + + if (fscanf(fp, "%d %d %d", &flags, &proto, &alg) != 3) { + EREPORT(("dst_read_public_key(): Can not read flag/proto/alg field from %s\n" + ,name)); + return (NULL); + } + /* read in the key string */ + fgets(enckey, sizeof(enckey), fp); + + /* If we aren't at end-of-file, something is wrong. */ + while ((c = getc(fp)) != EOF) + if (!isspace(c)) + break; + if (!feof(fp)) { + EREPORT(("Key too long in file: %s", name)); + return NULL; + } + fclose(fp); + + if ((len = strlen(enckey)) <= 0) + return (NULL); + + /* discard \n */ + enckey[--len] = '\0'; + + /* remove leading spaces */ + for (notspace = (char *) enckey; isspace((*notspace)&0xff); len--) + notspace++; + + dlen = b64_pton(notspace, deckey, sizeof(deckey)); + if (dlen < 0) { + EREPORT(("dst_read_public_key: bad return from b64_pton = %d", + dlen)); + return (NULL); + } + /* store key and info in a key structure that is returned */ +/* return dst_store_public_key(in_name, alg, proto, 666, flags, deckey, + dlen);*/ + return dst_buffer_to_key(in_name, alg, flags, proto, deckey, dlen); +} +#endif + +#ifdef _UNUSED_API_ +/* + * dst_write_public_key + * Write a key to disk in DNS format. + * Parameters + * key Pointer to a DST key structure. + * Returns + * 0 Failure + * 1 Success + */ + +static int +dst_s_write_public_key(const DST_KEY *key) +{ + FILE *fp; + char filename[PATH_MAX]; + u_char out_key[RAW_KEY_SIZE]; + char enc_key[RAW_KEY_SIZE]; + int len = 0; + int mode; + + memset(out_key, 0, sizeof(out_key)); + if (key == NULL) { + EREPORT(("dst_write_public_key(): No key specified \n")); + return (0); + } else if ((len = dst_key_to_dnskey(key, out_key, sizeof(out_key)))< 0) + return (0); + + /* Make the filename */ + if (dst_s_build_filename(filename, key->dk_key_name, key->dk_id, + key->dk_alg, PUBLIC_KEY, PATH_MAX) == -1) { + EREPORT(("dst_write_public_key(): Cannot make filename from %s, %d, and %s\n", + key->dk_key_name, key->dk_id, PUBLIC_KEY)); + return (0); + } + /* XXX in general this should be a check for symmetric keys */ + mode = (key->dk_alg == KEY_HMAC_MD5) ? 0600 : 0644; + /* create public key file */ + if ((fp = dst_s_fopen(filename, "w+", mode)) == NULL) { + EREPORT(("DST_write_public_key: open of file:%s failed (errno=%d)\n", + filename, errno)); + return (0); + } + /*write out key first base64 the key data */ + if (key->dk_flags & DST_EXTEND_FLAG) + b64_ntop(&out_key[6], len - 6, enc_key, sizeof(enc_key)); + else + b64_ntop(&out_key[4], len - 4, enc_key, sizeof(enc_key)); + fprintf(fp, "%s IN KEY %d %d %d %s\n", + key->dk_key_name, + key->dk_flags, key->dk_proto, key->dk_alg, enc_key); + fclose(fp); + return (1); +} +#endif + +#ifdef _UNUSED_API_ +/* + * dst_dnskey_to_public_key + * This function converts the contents of a DNS KEY RR into a DST + * key structure. + * Paramters + * len Length of the RDATA of the KEY RR RDATA + * rdata A pointer to the the KEY RR RDATA. + * in_name Key name to be stored in key structure. + * Returns + * NULL Failure + * NON-NULL Success. Pointer to key structure. + * Caller's responsibility to free() it. + */ + +DST_KEY * +dst_dnskey_to_key(const char *in_name, const u_char *rdata, const int len) +{ + DST_KEY *key_st; + int alg ; + int start = DST_KEY_START; + + if (rdata == NULL || len <= DST_KEY_ALG) /* no data */ + return (NULL); + alg = (u_int8_t) rdata[DST_KEY_ALG]; + if (!dst_check_algorithm(alg)) { /* make sure alg is available */ + EREPORT(("dst_dnskey_to_key(): Algorithm %d not suppored\n", + alg)); + return (NULL); + } + if ((key_st = dst_s_get_key_struct(in_name, alg, 0, 0, 0)) == NULL) + return (NULL); + + if (in_name == NULL) + return (NULL); + key_st->dk_id = dst_s_dns_key_id(rdata, len); + key_st->dk_flags = dst_s_get_int16(rdata); + key_st->dk_proto = (u_int16_t) rdata[DST_KEY_PROT]; + if (key_st->dk_flags & DST_EXTEND_FLAG) { + u_int32_t ext_flags; + ext_flags = (u_int32_t) dst_s_get_int16(&rdata[DST_EXT_FLAG]); + key_st->dk_flags = key_st->dk_flags | (ext_flags << 16); + start += 2; + } + /* + * now point to the begining of the data representing the encoding + * of the key + */ + if (key_st->dk_func && key_st->dk_func->from_dns_key) { + if (key_st->dk_func->from_dns_key(key_st, &rdata[start], + len - start) > 0) + return (key_st); + } else + EREPORT(("dst_dnskey_to_public_key(): unsuppored alg %d\n", + alg)); + + SAFE_FREE(key_st); + return (key_st); +} +#endif + +/* + * dst_public_key_to_dnskey + * Function to encode a public key into DNS KEY wire format + * Parameters + * key Key structure to encode. + * out_storage Location to write the encoded key to. + * out_len Size of the output array. + * Returns + * <0 Failure + * >=0 Number of bytes written to out_storage + */ + +#ifdef __APPLE__ +static +#endif +int +dst_key_to_dnskey(const DST_KEY *key, u_char *out_storage, + const int out_len) +{ + u_int16_t val; + int loc = 0; + int enc_len = 0; + if (key == NULL) + return (-1); + + if (!dst_check_algorithm(key->dk_alg)) { /* make sure alg is available */ + EREPORT(("dst_key_to_dnskey(): Algorithm %d not suppored\n", + key->dk_alg)); + return (UNSUPPORTED_KEYALG); + } + memset(out_storage, 0, out_len); + val = (u_int16_t)(key->dk_flags & 0xffff); + dst_s_put_int16(out_storage, val); + loc += 2; + + out_storage[loc++] = (u_char) key->dk_proto; + out_storage[loc++] = (u_char) key->dk_alg; + + if (key->dk_flags > 0xffff) { /* Extended flags */ + val = (u_int16_t)((key->dk_flags >> 16) & 0xffff); + dst_s_put_int16(&out_storage[loc], val); + loc += 2; + } + if (key->dk_KEY_struct == NULL) + return (loc); + if (key->dk_func && key->dk_func->to_dns_key) { + enc_len = key->dk_func->to_dns_key(key, + (u_char *) &out_storage[loc], + out_len - loc); + if (enc_len > 0) + return (enc_len + loc); + else + return (-1); + } else + EREPORT(("dst_key_to_dnskey(): Unsupported ALG %d\n", + key->dk_alg)); + return (-1); +} + +/* + * dst_buffer_to_key + * Function to encode a string of raw data into a DST key + * Parameters + * alg The algorithm (HMAC only) + * key A pointer to the data + * keylen The length of the data + * Returns + * NULL an error occurred + * NON-NULL the DST key + */ +DST_KEY * +dst_buffer_to_key(const char *key_name, /* name of the key */ + const int alg, /* algorithm */ + const int flags, /* dns flags */ + const int protocol, /* dns protocol */ + const u_char *key_buf, /* key in dns wire fmt */ + const int key_len) /* size of key */ +{ + + DST_KEY *dkey = NULL; + int dnslen; + u_char dns[2048]; + + if (!dst_check_algorithm(alg)) { /* make sure alg is available */ + EREPORT(("dst_buffer_to_key(): Algorithm %d not suppored\n", alg)); + return (NULL); + } + + dkey = dst_s_get_key_struct(key_name, alg, flags, + protocol, -1); + + if (dkey == NULL) + return (NULL); + if (dkey->dk_func == NULL || dkey->dk_func->from_dns_key == NULL) + return NULL; + + if (dkey->dk_func->from_dns_key(dkey, key_buf, key_len) < 0) { + EREPORT(("dst_buffer_to_key(): dst_buffer_to_hmac failed\n")); + return (dst_free_key(dkey)); + } + + dnslen = dst_key_to_dnskey(dkey, dns, sizeof(dns)); + dkey->dk_id = dst_s_dns_key_id(dns, dnslen); + return (dkey); +} + +#ifdef _UNUSED_API_ +int +dst_key_to_buffer(DST_KEY *key, u_char *out_buff, int buf_len) +{ + int len; + /* this function will extrac the secret of HMAC into a buffer */ + if (key == NULL) + return (0); + if (key->dk_func != NULL && key->dk_func->to_dns_key != NULL) { + len = key->dk_func->to_dns_key(key, out_buff, buf_len); + if (len < 0) + return (0); + return (len); + } + return (0); +} +#endif + +#ifdef _UNUSED_API_ +/* + * dst_s_read_private_key_file + * Function reads in private key from a file. + * Fills out the KEY structure. + * Parameters + * name Name of the key to be read. + * pk_key Structure that the key is returned in. + * in_id Key identifier (tag) + * Return + * 1 if everthing works + * 0 if there is any problem + */ + +static int +dst_s_read_private_key_file(char *name, DST_KEY *pk_key, u_int16_t in_id, + int in_alg) +{ + int cnt, alg, len, major, minor, file_major, file_minor; + int ret, id; + char filename[PATH_MAX]; + u_char in_buff[RAW_KEY_SIZE], *p; + FILE *fp; + int dnslen; + u_char dns[2048]; + + if (name == NULL || pk_key == NULL) { + EREPORT(("dst_read_private_key_file(): No key name given\n")); + return (0); + } + /* Make the filename */ + if (dst_s_build_filename(filename, name, in_id, in_alg, PRIVATE_KEY, + PATH_MAX) == -1) { + EREPORT(("dst_read_private_key(): Cannot make filename from %s, %d, and %s\n", + name, in_id, PRIVATE_KEY)); + return (0); + } + /* first check if we can find the key file */ + if ((fp = dst_s_fopen(filename, "r", 0)) == NULL) { + EREPORT(("dst_s_read_private_key_file: Could not open file %s in directory %s\n", + filename, dst_path[0] ? dst_path : + (char *) getcwd(NULL, PATH_MAX - 1))); + return (0); + } + /* now read the header info from the file */ + if ((cnt = fread(in_buff, 1, sizeof(in_buff), fp)) < 5) { + fclose(fp); + EREPORT(("dst_s_read_private_key_file: error reading file %s (empty file)\n", + filename)); + return (0); + } + /* decrypt key */ + fclose(fp); + if (memcmp(in_buff, "Private-key-format: v", 20) != 0) + goto fail; + len = cnt; + p = in_buff; + + if (!dst_s_verify_str((const char **) &p, "Private-key-format: v")) { + EREPORT(("dst_s_read_private_key_file(): Not a Key file/Decrypt failed %s\n", name)); + goto fail; + } + /* read in file format */ + sscanf((char *)p, "%d.%d", &file_major, &file_minor); + sscanf(KEY_FILE_FORMAT, "%d.%d", &major, &minor); + if (file_major < 1) { + EREPORT(("dst_s_read_private_key_file(): Unknown keyfile %d.%d version for %s\n", + file_major, file_minor, name)); + goto fail; + } else if (file_major > major || file_minor > minor) + EREPORT(( + "dst_s_read_private_key_file(): Keyfile %s version higher than mine %d.%d MAY FAIL\n", + name, file_major, file_minor)); + + while (*p++ != '\n') ; /* skip to end of line */ + + if (!dst_s_verify_str((const char **) &p, "Algorithm: ")) + goto fail; + + if (sscanf((char *)p, "%d", &alg) != 1) + goto fail; + while (*p++ != '\n') ; /* skip to end of line */ + + if (pk_key->dk_key_name && !strcmp(pk_key->dk_key_name, name)) + SAFE_FREE2(pk_key->dk_key_name, strlen(pk_key->dk_key_name)); + pk_key->dk_key_name = (char *) strdup(name); + + /* allocate and fill in key structure */ + if (pk_key->dk_func == NULL || pk_key->dk_func->from_file_fmt == NULL) + goto fail; + + ret = pk_key->dk_func->from_file_fmt(pk_key, (char *)p, &in_buff[len] - p); + if (ret < 0) + goto fail; + + dnslen = dst_key_to_dnskey(pk_key, dns, sizeof(dns)); + id = dst_s_dns_key_id(dns, dnslen); + + /* Make sure the actual key tag matches the input tag used in the filename + */ + if (id != in_id) { + EREPORT(("dst_s_read_private_key_file(): actual tag of key read %d != input tag used to build filename %d.\n", id, in_id)); + goto fail; + } + pk_key->dk_id = (u_int16_t) id; + pk_key->dk_alg = alg; + memset(in_buff, 0, cnt); + return (1); + + fail: + memset(in_buff, 0, cnt); + return (0); +} +#endif + +#ifdef _UNUSED_API_ +/* + * dst_generate_key + * Generate and store a public/private keypair. + * Keys will be stored in formatted files. + * Parameters + * name Name of the new key. Used to create key files + * K++.public and K++.private. + * bits Size of the new key in bits. + * exp What exponent to use: + * 0 use exponent 3 + * non-zero use Fermant4 + * flags The default value of the DNS Key flags. + * The DNS Key RR Flag field is defined in RFC 2065, + * section 3.3. The field has 16 bits. + * protocol + * Default value of the DNS Key protocol field. + * The DNS Key protocol field is defined in RFC 2065, + * section 3.4. The field has 8 bits. + * alg What algorithm to use. Currently defined: + * KEY_RSA 1 + * KEY_DSA 3 + * KEY_HMAC 157 + * out_id The key tag is returned. + * + * Return + * NULL Failure + * non-NULL the generated key pair + * Caller frees the result, and its dk_name pointer. + */ +DST_KEY * +dst_generate_key(const char *name, const int bits, const int exp, + const int flags, const int protocol, const int alg) +{ + DST_KEY *new_key = NULL; + int res; + int dnslen; + u_char dns[2048]; + + if (name == NULL) + return (NULL); + + if (!dst_check_algorithm(alg)) { /* make sure alg is available */ + EREPORT(("dst_generate_key(): Algorithm %d not suppored\n", alg)); + return (NULL); + } + + new_key = dst_s_get_key_struct(name, alg, flags, protocol, bits); + if (new_key == NULL) + return (NULL); + if (bits == 0) /* null key we are done */ + return (new_key); + if (new_key->dk_func == NULL || new_key->dk_func->generate == NULL) { + EREPORT(("dst_generate_key_pair():Unsupported algorithm %d\n", + alg)); + return (dst_free_key(new_key)); + } + if ((res = new_key->dk_func->generate(new_key, exp)) <= 0) { + EREPORT(("dst_generate_key_pair(): Key generation failure %s %d %d %d\n", + new_key->dk_key_name, new_key->dk_alg, + new_key->dk_key_size, exp)); + return (dst_free_key(new_key)); + } + + dnslen = dst_key_to_dnskey(new_key, dns, sizeof(dns)); + if (dnslen != UNSUPPORTED_KEYALG) + new_key->dk_id = dst_s_dns_key_id(dns, dnslen); + else + new_key->dk_id = 0; + + return (new_key); +} +#endif + +/* + * dst_free_key + * Release all data structures pointed to by a key structure. + * Parameters + * f_key Key structure to be freed. + */ + +DST_KEY * +dst_free_key(DST_KEY *f_key) +{ + + if (f_key == NULL) + return (f_key); + if (f_key->dk_func && f_key->dk_func->destroy) + f_key->dk_KEY_struct = + f_key->dk_func->destroy(f_key->dk_KEY_struct); + else { + EREPORT(("dst_free_key(): Unknown key alg %d\n", + f_key->dk_alg)); + free(f_key->dk_KEY_struct); /* SHOULD NOT happen */ + } + if (f_key->dk_KEY_struct) { + free(f_key->dk_KEY_struct); + f_key->dk_KEY_struct = NULL; + } + if (f_key->dk_key_name) + SAFE_FREE(f_key->dk_key_name); + SAFE_FREE(f_key); + return (NULL); +} + +#ifdef _UNUSED_API_ +/* + * dst_sig_size + * Return the maximim size of signature from the key specified in bytes + * Parameters + * key + * Returns + * bytes + */ +int +dst_sig_size(DST_KEY *key) { + switch (key->dk_alg) { + case KEY_HMAC_MD5: + return (16); + case KEY_HMAC_SHA1: + return (20); + case KEY_RSA: + return (key->dk_key_size + 7) / 8; + case KEY_DSA: + return (40); + default: + EREPORT(("dst_sig_size(): Unknown key alg %d\n", key->dk_alg)); + return -1; + } +} +#endif diff --git a/dst_hmac_link.c b/dst_hmac_link.c new file mode 100644 index 0000000..b5b3375 --- /dev/null +++ b/dst_hmac_link.c @@ -0,0 +1,481 @@ +#ifdef HMAC_MD5 +#ifndef __APPLE__ +#ifndef LINT +static const char rcsid[] = "$Header: /Users/Shared/libresolv_2/libresolv/dst_hmac_link.c,v 1.1 2006/03/01 19:01:36 majka Exp $"; +#endif +#endif + +/* + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * Permission to use, copy modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. + */ + +/* + * This file contains an implementation of the HMAC-MD5 algorithm. + */ +#ifndef __APPLE__ +#include "port_before.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dst_internal.h" +#ifdef USE_MD5 +# include "md5.h" +# ifndef _MD5_H_ +# define _MD5_H_ 1 /* make sure we do not include rsaref md5.h file */ +# endif +#endif + +#ifndef __APPLE__ +#include "port_after.h" +#endif + + +#define HMAC_LEN 64 +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c +#define MD5_LEN 16 + + +typedef struct hmackey { + u_char hk_ipad[64], hk_opad[64]; +} HMAC_Key; + + +/************************************************************************** + * dst_hmac_md5_sign + * Call HMAC signing functions to sign a block of data. + * There are three steps to signing, INIT (initialize structures), + * UPDATE (hash (more) data), FINAL (generate a signature). This + * routine performs one or more of these steps. + * Parameters + * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. + * priv_key key to use for signing. + * context the context to be used in this digest + * data data to be signed. + * len length in bytes of data. + * signature location to store signature. + * sig_len size of the signature location + * returns + * N Success on SIG_MODE_FINAL = returns signature length in bytes + * 0 Success on SIG_MODE_INIT and UPDATE + * <0 Failure + */ + +static int +dst_hmac_md5_sign(const int mode, DST_KEY *d_key, void **context, + const u_char *data, const int len, + u_char *signature, const int sig_len) +{ + HMAC_Key *key; + int sign_len = 0; + MD5_CTX *ctx = NULL; + + if (mode & SIG_MODE_INIT) + ctx = (MD5_CTX *) malloc(sizeof(*ctx)); + else if (context) + ctx = (MD5_CTX *) *context; + if (ctx == NULL) + return (-1); + + if (d_key == NULL || d_key->dk_KEY_struct == NULL) + return (-1); + key = (HMAC_Key *) d_key->dk_KEY_struct; + + if (mode & SIG_MODE_INIT) { + MD5Init(ctx); + MD5Update(ctx, key->hk_ipad, HMAC_LEN); + } + + if ((mode & SIG_MODE_UPDATE) && (data && len > 0)) + MD5Update(ctx, data, len); + + if (mode & SIG_MODE_FINAL) { + if (signature == NULL || sig_len < MD5_LEN) + return (SIGN_FINAL_FAILURE); + MD5Final(signature, ctx); + + /* perform outer MD5 */ + MD5Init(ctx); + MD5Update(ctx, key->hk_opad, HMAC_LEN); + MD5Update(ctx, signature, MD5_LEN); + MD5Final(signature, ctx); + sign_len = MD5_LEN; + SAFE_FREE(ctx); + } + else { + if (context == NULL) + return (-1); + *context = (void *) ctx; + } + return (sign_len); +} + + +/************************************************************************** + * dst_hmac_md5_verify() + * Calls HMAC verification routines. There are three steps to + * verification, INIT (initialize structures), UPDATE (hash (more) data), + * FINAL (generate a signature). This routine performs one or more of + * these steps. + * Parameters + * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL. + * dkey key to use for verify. + * data data signed. + * len length in bytes of data. + * signature signature. + * sig_len length in bytes of signature. + * returns + * 0 Success + * <0 Failure + */ + +static int +dst_hmac_md5_verify(const int mode, DST_KEY *d_key, void **context, + const u_char *data, const int len, + const u_char *signature, const int sig_len) +{ + HMAC_Key *key; + MD5_CTX *ctx = NULL; + + if (mode & SIG_MODE_INIT) + ctx = (MD5_CTX *) malloc(sizeof(*ctx)); + else if (context) + ctx = (MD5_CTX *) *context; + if (ctx == NULL) + return (-1); + + if (d_key == NULL || d_key->dk_KEY_struct == NULL) + return (-1); + + key = (HMAC_Key *) d_key->dk_KEY_struct; + if (mode & SIG_MODE_INIT) { + MD5Init(ctx); + MD5Update(ctx, key->hk_ipad, HMAC_LEN); + } + if ((mode & SIG_MODE_UPDATE) && (data && len > 0)) + MD5Update(ctx, data, len); + + if (mode & SIG_MODE_FINAL) { + u_char digest[MD5_LEN]; + if (signature == NULL || key == NULL || sig_len != MD5_LEN) + return (VERIFY_FINAL_FAILURE); + MD5Final(digest, ctx); + + /* perform outer MD5 */ + MD5Init(ctx); + MD5Update(ctx, key->hk_opad, HMAC_LEN); + MD5Update(ctx, digest, MD5_LEN); + MD5Final(digest, ctx); + + SAFE_FREE(ctx); + if (memcmp(digest, signature, MD5_LEN) != 0) + return (VERIFY_FINAL_FAILURE); + } + else { + if (context == NULL) + return (-1); + *context = (void *) ctx; + } + return (0); +} + + +/************************************************************************** + * dst_buffer_to_hmac_md5 + * Converts key from raw data to an HMAC Key + * This function gets in a pointer to the data + * Parameters + * hkey the HMAC key to be filled in + * key the key in raw format + * keylen the length of the key + * Return + * 0 Success + * <0 Failure + */ +static int +dst_buffer_to_hmac_md5(DST_KEY *dkey, const u_char *key, const int keylen) +{ + int i; + HMAC_Key *hkey = NULL; + MD5_CTX ctx; + int local_keylen = keylen; + + if (dkey == NULL || key == NULL || keylen < 0) + return (-1); + + if ((hkey = (HMAC_Key *) malloc(sizeof(HMAC_Key))) == NULL) + return (-2); + + memset(hkey->hk_ipad, 0, sizeof(hkey->hk_ipad)); + memset(hkey->hk_opad, 0, sizeof(hkey->hk_opad)); + + /* if key is longer than HMAC_LEN bytes reset it to key=MD5(key) */ + if (keylen > HMAC_LEN) { + u_char tk[MD5_LEN]; + MD5Init(&ctx); + MD5Update(&ctx, key, keylen); + MD5Final(tk, &ctx); + memset((void *) &ctx, 0, sizeof(ctx)); + key = tk; + local_keylen = MD5_LEN; + } + /* start out by storing key in pads */ + memcpy(hkey->hk_ipad, key, local_keylen); + memcpy(hkey->hk_opad, key, local_keylen); + + /* XOR key with hk_ipad and opad values */ + for (i = 0; i < HMAC_LEN; i++) { + hkey->hk_ipad[i] ^= HMAC_IPAD; + hkey->hk_opad[i] ^= HMAC_OPAD; + } + dkey->dk_key_size = local_keylen; + dkey->dk_KEY_struct = (void *) hkey; + return (1); +} + + +/************************************************************************** + * dst_hmac_md5_key_to_file_format + * Encodes an HMAC Key into the portable file format. + * Parameters + * hkey HMAC KEY structure + * buff output buffer + * buff_len size of output buffer + * Return + * 0 Failure - null input hkey + * -1 Failure - not enough space in output area + * N Success - Length of data returned in buff + */ + +static int +dst_hmac_md5_key_to_file_format(const DST_KEY *dkey, char *buff, + const int buff_len) +{ + char *bp; + int len, b_len, i, key_len; + u_char key[HMAC_LEN]; + HMAC_Key *hkey; + + if (dkey == NULL || dkey->dk_KEY_struct == NULL) + return (0); + if (buff == NULL || buff_len <= (int) strlen(key_file_fmt_str)) + return (-1); /* no OR not enough space in output area */ + + hkey = (HMAC_Key *) dkey->dk_KEY_struct; + memset(buff, 0, buff_len); /* just in case */ + /* write file header */ + sprintf(buff, key_file_fmt_str, KEY_FILE_FORMAT, KEY_HMAC_MD5, "HMAC"); + + bp = (char *) strchr(buff, '\0'); + b_len = buff_len - (bp - buff); + + memset(key, 0, HMAC_LEN); + for (i = 0; i < HMAC_LEN; i++) + key[i] = hkey->hk_ipad[i] ^ HMAC_IPAD; + for (i = HMAC_LEN - 1; i >= 0; i--) + if (key[i] != 0) + break; + key_len = i + 1; + + strcat(bp, "Key: "); + bp += strlen("Key: "); + b_len = buff_len - (bp - buff); + + len = b64_ntop(key, key_len, bp, b_len); + if (len < 0) + return (-1); + bp += len; + *(bp++) = '\n'; + *bp = '\0'; + b_len = buff_len - (bp - buff); + + return (buff_len - b_len); +} + + +/************************************************************************** + * dst_hmac_md5_key_from_file_format + * Converts contents of a key file into an HMAC key. + * Parameters + * hkey structure to put key into + * buff buffer containing the encoded key + * buff_len the length of the buffer + * Return + * n >= 0 Foot print of the key converted + * n < 0 Error in conversion + */ + +static int +dst_hmac_md5_key_from_file_format(DST_KEY *dkey, const char *buff, + const int buff_len) +{ + const char *p = buff, *eol; + u_char key[HMAC_LEN+1]; /* b64_pton needs more than 64 bytes do decode + * it should probably be fixed rather than doing + * this + */ + u_char *tmp; + int key_len, len; + + if (dkey == NULL) + return (-2); + if (buff == NULL || buff_len < 0) + return (-1); + + memset(key, 0, sizeof(key)); + + if (!dst_s_verify_str(&p, "Key: ")) + return (-3); + + eol = strchr(p, '\n'); + if (eol == NULL) + return (-4); + len = eol - p; + tmp = malloc(len + 2); + memcpy(tmp, p, len); + *(tmp + len) = 0x0; + key_len = b64_pton((char *)tmp, key, HMAC_LEN+1); /* see above */ + SAFE_FREE2(tmp, len + 2); + + if (dst_buffer_to_hmac_md5(dkey, key, key_len) < 0) { + return (-6); + } + return (0); +} + +/* + * dst_hmac_md5_to_dns_key() + * function to extract hmac key from DST_KEY structure + * intput: + * in_key: HMAC-MD5 key + * output: + * out_str: buffer to write ot + * out_len: size of output buffer + * returns: + * number of bytes written to output buffer + */ +static int +dst_hmac_md5_to_dns_key(const DST_KEY *in_key, u_char *out_str, + const int out_len) +{ + + HMAC_Key *hkey; + int i; + + if (in_key == NULL || in_key->dk_KEY_struct == NULL || + out_len <= in_key->dk_key_size || out_str == NULL) + return (-1); + + hkey = (HMAC_Key *) in_key->dk_KEY_struct; + for (i = 0; i < in_key->dk_key_size; i++) + out_str[i] = hkey->hk_ipad[i] ^ HMAC_IPAD; + return (i); +} + +/************************************************************************** + * dst_hmac_md5_compare_keys + * Compare two keys for equality. + * Return + * 0 The keys are equal + * NON-ZERO The keys are not equal + */ + +static int +dst_hmac_md5_compare_keys(const DST_KEY *key1, const DST_KEY *key2) +{ + HMAC_Key *hkey1 = (HMAC_Key *) key1->dk_KEY_struct; + HMAC_Key *hkey2 = (HMAC_Key *) key2->dk_KEY_struct; + return memcmp(hkey1->hk_ipad, hkey2->hk_ipad, HMAC_LEN); +} + +/************************************************************************** + * dst_hmac_md5_free_key_structure + * Frees all (none) dynamically allocated structures in hkey + */ + +static void * +dst_hmac_md5_free_key_structure(void *key) +{ + HMAC_Key *hkey = key; + SAFE_FREE(hkey); + return (NULL); +} + + +/*************************************************************************** + * dst_hmac_md5_generate_key + * Creates a HMAC key of size size with a maximum size of 63 bytes + * generating a HMAC key larger than 63 bytes makes no sense as that key + * is digested before use. + */ + +static int +dst_hmac_md5_generate_key(DST_KEY *key, const int nothing) +{ + (void)key; + (void)nothing; + return (-1); +} + +/* + * dst_hmac_md5_init() Function to answer set up function pointers for HMAC + * related functions + */ +int +dst_hmac_md5_init() +{ + if (dst_t_func[KEY_HMAC_MD5] != NULL) + return (1); + dst_t_func[KEY_HMAC_MD5] = malloc(sizeof(struct dst_func)); + if (dst_t_func[KEY_HMAC_MD5] == NULL) + return (0); + memset(dst_t_func[KEY_HMAC_MD5], 0, sizeof(struct dst_func)); + dst_t_func[KEY_HMAC_MD5]->sign = dst_hmac_md5_sign; + dst_t_func[KEY_HMAC_MD5]->verify = dst_hmac_md5_verify; + dst_t_func[KEY_HMAC_MD5]->compare = dst_hmac_md5_compare_keys; + dst_t_func[KEY_HMAC_MD5]->generate = dst_hmac_md5_generate_key; + dst_t_func[KEY_HMAC_MD5]->destroy = dst_hmac_md5_free_key_structure; + dst_t_func[KEY_HMAC_MD5]->to_dns_key = dst_hmac_md5_to_dns_key; + dst_t_func[KEY_HMAC_MD5]->from_dns_key = dst_buffer_to_hmac_md5; + dst_t_func[KEY_HMAC_MD5]->to_file_fmt = dst_hmac_md5_key_to_file_format; + dst_t_func[KEY_HMAC_MD5]->from_file_fmt = dst_hmac_md5_key_from_file_format; + return (1); +} + +#else +#define dst_hmac_md5_init res_9_dst_hmac_md5_init +int +dst_hmac_md5_init(){ + return (0); +} +#endif + + + + + + + diff --git a/dst_internal.h b/dst_internal.h new file mode 100644 index 0000000..177af3a --- /dev/null +++ b/dst_internal.h @@ -0,0 +1,188 @@ +#ifndef DST_INTERNAL_H +#define DST_INTERNAL_H + +/* + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * Permission to use, copy modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. + */ +#include +#include +#if (!defined(BSD)) || (BSD < 199306) +# include +#else +# include +#endif + +#ifndef PATH_MAX +# ifdef POSIX_PATH_MAX +# define PATH_MAX POSIX_PATH_MAX +# else +# define PATH_MAX 255 /* this is the value of POSIX_PATH_MAX */ +# endif +#endif + +#ifndef dst_func +#define dst_func res_9_dst_func +#endif +#define DST_KEY res_9_DST_KEY +typedef struct dst_key { + char *dk_key_name; /* name of the key */ + int dk_key_size; /* this is the size of the key in bits */ + int dk_proto; /* what protocols this key can be used for */ + int dk_alg; /* algorithm number from key record */ + u_int32_t dk_flags; /* and the flags of the public key */ + u_int16_t dk_id; /* identifier of the key */ + void *dk_KEY_struct; /* pointer to key in crypto pkg fmt */ + struct dst_func *dk_func; /* point to cryptto pgk specific function table */ +} DST_KEY; +#define HAS_DST_KEY + +#ifdef __APPLE__ +#include "dst.h" +#else +#include +#endif + +/* + * define what crypto systems are supported for RSA, + * BSAFE is prefered over RSAREF; only one can be set at any time + */ +#if defined(BSAFE) && defined(RSAREF) +# error "Cannot have both BSAFE and RSAREF defined" +#endif + +/* Declare dst_lib specific constants */ +#define KEY_FILE_FORMAT "1.2" + +/* suffixes for key file names */ +#define PRIVATE_KEY "private" +#define PUBLIC_KEY "key" + +/* error handling */ +#ifdef REPORT_ERRORS +#define EREPORT(str) printf str +#else +#define EREPORT(str) (void)0 +#endif + +/* use our own special macro to FRRE memory */ + +#ifndef SAFE_FREE +#define SAFE_FREE(a) \ +do{if(a != NULL){memset(a,0, sizeof(*a)); free(a); a=NULL;}} while (0) +#define SAFE_FREE2(a,s) if (a != NULL && s > 0){memset(a,0, s);free(a); a=NULL;} +#endif + +#define dst_func res_9_dst_func +typedef struct dst_func { + int (*sign)(const int mode, DST_KEY *key, void **context, + const u_int8_t *data, const int len, + u_int8_t *signature, const int sig_len); + int (*verify)(const int mode, DST_KEY *key, void **context, + const u_int8_t *data, const int len, + const u_int8_t *signature, const int sig_len); + int (*compare)(const DST_KEY *key1, const DST_KEY *key2); + int (*generate)(DST_KEY *key, int parms); + void *(*destroy)(void *key); + /* conversion functions */ + int (*to_dns_key)(const DST_KEY *key, u_int8_t *out, + const int out_len); + int (*from_dns_key)(DST_KEY *key, const u_int8_t *str, + const int str_len); + int (*to_file_fmt)(const DST_KEY *key, char *out, + const int out_len); + int (*from_file_fmt)(DST_KEY *key, const char *out, + const int out_len); + +} dst_func; + +#define dst_t_func res_9_dst_t_func +extern dst_func *dst_t_func[DST_MAX_ALGS]; + +#define key_file_fmt_str res_9_key_file_fmt_str +extern const char *key_file_fmt_str; + +#define dst_path res_9_dst_path +extern const char *dst_path; + +#ifndef DST_HASH_SIZE +#define DST_HASH_SIZE 20 /* RIPEMD160 and SHA-1 are 20 bytes MD5 is 16 */ +#endif + +#define dst_bsafe_init res_9_dst_bsafe_init +int dst_bsafe_init(void); + +#define dst_rsaref_init res_9_dst_rsaref_init +int dst_rsaref_init(void); + +#define dst_hmac_md5_init res_9_dst_hmac_md5_init +int dst_hmac_md5_init(void); + +#define dst_cylink_init res_9_dst_cylink_init +int dst_cylink_init(void); + +#define dst_eay_dss_init res_9_dst_eay_dss_init +int dst_eay_dss_init(void); + +/* from higher level support routines */ +#define dst_s_calculate_bits res_9_dst_s_calculate_bits +int dst_s_calculate_bits( const u_int8_t *str, const int max_bits); + +#define dst_s_verify_str res_9_dst_s_verify_str +int dst_s_verify_str( const char **buf, const char *str); + + +/* conversion between dns names and key file names */ +#define dst_s_filename_length res_9_dst_s_filename_length +size_t dst_s_filename_length( const char *name, const char *suffix); + +#define dst_s_build_filename res_9_dst_s_build_filename +int dst_s_build_filename( char *filename, const char *name, + u_int16_t id, int alg, const char *suffix, + size_t filename_length); + +#define dst_s_fopen res_9_dst_s_fopen +FILE *dst_s_fopen (const char *filename, const char *mode, int perm); + +/* + * read and write network byte order into u_int?_t + * all of these should be retired + */ +#define dst_s_get_int16 res_9_dst_s_get_int16 +u_int16_t dst_s_get_int16( const u_int8_t *buf); + +#define dst_s_put_int16 res_9_dst_s_put_int16 +void dst_s_put_int16( u_int8_t *buf, const u_int16_t val); + +#define dst_s_get_int32 res_9_dst_s_get_int32 +u_int32_t dst_s_get_int32( const u_int8_t *buf); + +#define dst_s_put_int32 res_9_dst_s_put_int32 +void dst_s_put_int32( u_int8_t *buf, const u_int32_t val); + +#ifdef DUMP +# undef DUMP +# define DUMP(a,b,c,d) dst_s_dump(a,b,c,d) +#else +# define DUMP(a,b,c,d) +#endif +#define dst_s_dump res_9_dst_s_dump +void +dst_s_dump(const int mode, const u_char *data, const int size, + const char *msg); + + + +#endif /* DST_INTERNAL_H */ diff --git a/dst_support.c b/dst_support.c new file mode 100644 index 0000000..eddccc1 --- /dev/null +++ b/dst_support.c @@ -0,0 +1,367 @@ +#ifndef __APPLE__ +static const char rcsid[] = "$Header: /Users/Shared/libresolv_2/libresolv/dst_support.c,v 1.1 2006/03/01 19:01:36 majka Exp $"; +#endif + + +/* + * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc. + * + * Permission to use, copy modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THE SOFTWARE. + */ + +#ifndef __APPLE__ +#include "port_before.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dst_internal.h" + +#ifndef __APPLE__ +#include "port_after.h" +#endif + +/* + * dst_s_verify_str() + * Validate that the input string(*str) is at the head of the input + * buffer(**buf). If so, move the buffer head pointer (*buf) to + * the first byte of data following the string(*str). + * Parameters + * buf Input buffer. + * str Input string. + * Return + * 0 *str is not the head of **buff + * 1 *str is the head of **buff, *buf is is advanced to + * the tail of **buf. + */ + +int +dst_s_verify_str(const char **buf, const char *str) +{ + int b, s; + if (*buf == NULL) /* error checks */ + return (0); + if (str == NULL || *str == '\0') + return (1); + + b = strlen(*buf); /* get length of strings */ + s = strlen(str); + if (s > b || strncmp(*buf, str, s)) /* check if same */ + return (0); /* not a match */ + (*buf) += s; /* advance pointer */ + return (1); +} + +#ifdef _UNUSED_API_ +/* + * dst_s_calculate_bits + * Given a binary number represented in a u_char[], determine + * the number of significant bits used. + * Parameters + * str An input character string containing a binary number. + * max_bits The maximum possible significant bits. + * Return + * N The number of significant bits in str. + */ + +int +dst_s_calculate_bits(const u_char *str, const int max_bits) +{ + const u_char *p = str; + u_char i, j = 0x80; + int bits; + for (bits = max_bits; *p == 0x00 && bits > 0; p++) + bits -= 8; + for (i = *p; (i & j) != j; j >>= 1) + bits--; + return (bits); +} +#endif + +/* + * calculates a checksum used in dst for an id. + * takes an array of bytes and a length. + * returns a 16 bit checksum. + */ +#ifdef __APPLE__ +static +#endif +u_int16_t +dst_s_id_calc(const u_char *key, const int keysize) +{ + u_int32_t ac; + const u_char *kp = key; + int size = keysize; + + if (!key || (keysize <= 0)) + return (-1); + + for (ac = 0; size > 1; size -= 2, kp += 2) + ac += ((*kp) << 8) + *(kp + 1); + + if (size > 0) + ac += ((*kp) << 8); + ac += (ac >> 16) & 0xffff; + + return (ac & 0xffff); +} + +/* + * dst_s_dns_key_id() Function to calculate DNSSEC footprint from KEY record + * rdata + * Input: + * dns_key_rdata: the raw data in wire format + * rdata_len: the size of the input data + * Output: + * the key footprint/id calculated from the key data + */ +u_int16_t +dst_s_dns_key_id(const u_char *dns_key_rdata, const int rdata_len) +{ + if (!dns_key_rdata) + return 0; + + /* compute id */ + if (dns_key_rdata[3] == KEY_RSA) /* Algorithm RSA */ + return dst_s_get_int16((const u_char *) + &dns_key_rdata[rdata_len - 3]); + else if (dns_key_rdata[3] == KEY_HMAC_MD5) + /* compatibility */ + return 0; + else + /* compute a checksum on the key part of the key rr */ + return dst_s_id_calc(dns_key_rdata, rdata_len); +} + +/* + * dst_s_get_int16 + * This routine extracts a 16 bit integer from a two byte character + * string. The character string is assumed to be in network byte + * order and may be unaligned. The number returned is in host order. + * Parameter + * buf A two byte character string. + * Return + * The converted integer value. + */ + +u_int16_t +dst_s_get_int16(const u_char *buf) +{ + register u_int16_t a = 0; + a = ((u_int16_t)(buf[0] << 8)) | ((u_int16_t)(buf[1])); + return (a); +} + + +#ifdef _UNUSED_API_ +/* + * dst_s_get_int32 + * This routine extracts a 32 bit integer from a four byte character + * string. The character string is assumed to be in network byte + * order and may be unaligned. The number returned is in host order. + * Parameter + * buf A four byte character string. + * Return + * The converted integer value. + */ + +u_int32_t +dst_s_get_int32(const u_char *buf) +{ + register u_int32_t a = 0; + a = ((u_int32_t)(buf[0] << 24)) | ((u_int32_t)(buf[1] << 16)) | + ((u_int32_t)(buf[2] << 8)) | ((u_int32_t)(buf[3])); + return (a); +} +#endif + +/* + * dst_s_put_int16 + * Take a 16 bit integer and store the value in a two byte + * character string. The integer is assumed to be in network + * order and the string is returned in host order. + * + * Parameters + * buf Storage for a two byte character string. + * val 16 bit integer. + */ + +void +dst_s_put_int16(u_int8_t *buf, const u_int16_t val) +{ + buf[0] = (u_int8_t)(val >> 8); + buf[1] = (u_int8_t)(val); +} + +#ifdef _UNUSED_API_ +/* + * dst_s_put_int32 + * Take a 32 bit integer and store the value in a four byte + * character string. The integer is assumed to be in network + * order and the string is returned in host order. + * + * Parameters + * buf Storage for a four byte character string. + * val 32 bit integer. + */ + +void +dst_s_put_int32(u_int8_t *buf, const u_int32_t val) +{ + buf[0] = (u_int8_t)(val >> 24); + buf[1] = (u_int8_t)(val >> 16); + buf[2] = (u_int8_t)(val >> 8); + buf[3] = (u_int8_t)(val); +} +#endif + + +#ifdef _UNUSED_API_ +/* + * dst_s_filename_length + * + * This function returns the number of bytes needed to hold the + * filename for a key file. '/', '\' and ':' are not allowed. + * form: K++. + * + * Returns 0 if the filename would contain either '\', '/' or ':' + */ +size_t +dst_s_filename_length(const char *name, const char *suffix) +{ + if (name == NULL) + return (0); + if (strrchr(name, '\\')) + return (0); + if (strrchr(name, '/')) + return (0); + if (strrchr(name, ':')) + return (0); + if (suffix == NULL) + return (0); + if (strrchr(suffix, '\\')) + return (0); + if (strrchr(suffix, '/')) + return (0); + if (strrchr(suffix, ':')) + return (0); + return (1 + strlen(name) + 6 + strlen(suffix)); +} +#endif + +/* + * dst_s_build_filename () + * Builds a key filename from the key name, it's id, and a + * suffix. '\', '/' and ':' are not allowed. fA filename is of the + * form: K. + * form: K++. + * + * Returns -1 if the conversion fails: + * if the filename would be too long for space allotted + * if the filename would contain a '\', '/' or ':' + * Returns 0 on success + */ + +int +dst_s_build_filename(char *filename, const char *name, u_int16_t id, + int alg, const char *suffix, size_t filename_length) +{ + u_int32_t my_id; + if (filename == NULL) + return (-1); + memset(filename, 0, filename_length); + if (name == NULL) + return (-1); + if (suffix == NULL) + return (-1); + if (filename_length < 1 + strlen(name) + 4 + 6 + 1 + strlen(suffix)) + return (-1); + my_id = id; + sprintf(filename, "K%s+%03d+%05d.%s", name, alg, my_id, + (const char *) suffix); + if (strrchr(filename, '/')) + return (-1); + if (strrchr(filename, '\\')) + return (-1); + if (strrchr(filename, ':')) + return (-1); + return (0); +} + +/* + * dst_s_fopen () + * Open a file in the dst_path directory. If perm is specified, the + * file is checked for existence first, and not opened if it exists. + * Parameters + * filename File to open + * mode Mode to open the file (passed directly to fopen) + * perm File permission, if creating a new file. + * Returns + * NULL Failure + * NON-NULL (FILE *) of opened file. + */ +FILE * +dst_s_fopen(const char *filename, const char *mode, int perm) +{ + FILE *fp; + char pathname[PATH_MAX]; + size_t plen = sizeof(pathname); + + if (*dst_path != '\0') { + strcpy(pathname, dst_path); + plen -= strlen(pathname); + } + else + pathname[0] = '\0'; + + if (plen > strlen(filename)) + strncpy(&pathname[PATH_MAX - plen], filename, plen-1); + else + return (NULL); + + fp = fopen(pathname, mode); + if (perm) + chmod(pathname, perm); + return (fp); +} + +#ifdef _UNUSED_API_ +void +dst_s_dump(const int mode, const u_char *data, const int size, + const char *msg) +{ +#ifndef __APPLE__ + UNUSED(data); +#endif + + if (size > 0) { +#ifdef LONG_TEST + static u_char scratch[1000]; + int n ; + n = b64_ntop(data, scratch, size, sizeof(scratch)); + printf("%s: %x %d %s\n", msg, mode, n, scratch); +#else + printf("%s,%x %d\n", msg, mode, size); +#endif + } +} +#endif diff --git a/nameser.h b/nameser.h new file mode 100644 index 0000000..8f40203 --- /dev/null +++ b/nameser.h @@ -0,0 +1,588 @@ +/* + * Copyright (c) 1983, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * $Id: nameser.h,v 1.1 2006/03/01 19:01:36 majka Exp $ + */ + +#ifndef _NAMESER_9_H_ +#define _NAMESER_9_H_ + +#ifdef BIND_8_COMPAT +#include +#else + +#include +#if (!defined(BSD)) || (BSD < 199306) +# include +#else +# include +#endif +#include + +/* + * Revision information. This is the release date in YYYYMMDD format. + * It can change every day so the right thing to do with it is use it + * in preprocessor commands such as "#if (__NAMESER > 19931104)". Do not + * compare for equality; rather, use it to determine whether your libbind.a + * contains a new enough lib/nameser/ to support the feature you need. + */ + +#define __NAMESER 19991006 /* New interface version stamp. */ + +/* + * Define constants based on RFC 883, RFC 1034, RFC 1035 + */ +#define NS_PACKETSZ 512 /* maximum packet size */ +#define NS_MAXDNAME 1025 /* maximum domain name */ +#define NS_MAXCDNAME 255 /* maximum compressed domain name */ +#define NS_MAXLABEL 63 /* maximum length of domain label */ +#define NS_HFIXEDSZ 12 /* #/bytes of fixed data in header */ +#define NS_QFIXEDSZ 4 /* #/bytes of fixed data in query */ +#define NS_RRFIXEDSZ 10 /* #/bytes of fixed data in r record */ +#define NS_INT32SZ 4 /* #/bytes of data in a u_int32_t */ +#define NS_INT16SZ 2 /* #/bytes of data in a u_int16_t */ +#define NS_INT8SZ 1 /* #/bytes of data in a u_int8_t */ +#define NS_INADDRSZ 4 /* IPv4 T_A */ +#define NS_IN6ADDRSZ 16 /* IPv6 T_AAAA */ +#define NS_CMPRSFLGS 0xc0 /* Flag bits indicating name compression. */ +#define NS_DEFAULTPORT 53 /* For both TCP and UDP. */ + +/* + * These can be expanded with synonyms, just keep ns_parse.c:ns_parserecord() + * in synch with it. + */ +#define ns_sect res_9_ns_sect +typedef enum __ns_sect { + ns_s_qd = 0, /* Query: Question. */ + ns_s_zn = 0, /* Update: Zone. */ + ns_s_an = 1, /* Query: Answer. */ + ns_s_pr = 1, /* Update: Prerequisites. */ + ns_s_ns = 2, /* Query: Name servers. */ + ns_s_ud = 2, /* Update: Update. */ + ns_s_ar = 3, /* Query|Update: Additional records. */ + ns_s_max = 4 +} ns_sect; + +/* + * This is a message handle. It is caller allocated and has no dynamic data. + * This structure is intended to be opaque to all but ns_parse.c, thus the + * leading _'s on the member names. Use the accessor functions, not the _'s. + */ +#define ns_msg res_9_ns_msg +typedef struct __ns_msg { + const u_char *_msg, *_eom; + u_int16_t _id, _flags, _counts[ns_s_max]; + const u_char *_sections[ns_s_max]; + ns_sect _sect; + int _rrnum; + const u_char *_msg_ptr; +} ns_msg; + +/* Private data structure - do not use from outside library. */ +#define _ns_flagdata _res_9_ns_flagdata +struct _ns_flagdata { int mask, shift; }; +extern struct _ns_flagdata _ns_flagdata[]; + +/* Accessor macros - this is part of the public interface. */ + +#define ns_msg_id(handle) ((handle)._id + 0) +#define ns_msg_base(handle) ((handle)._msg + 0) +#define ns_msg_end(handle) ((handle)._eom + 0) +#define ns_msg_size(handle) ((handle)._eom - (handle)._msg) +#define ns_msg_count(handle, section) ((handle)._counts[section] + 0) + +/* + * This is a parsed record. It is caller allocated and has no dynamic data. + */ +#define ns_rr res_9_ns_rr + +typedef struct __ns_rr { + char name[NS_MAXDNAME]; + u_int16_t type; + u_int16_t rr_class; + u_int32_t ttl; + u_int16_t rdlength; + const u_char * rdata; +} ns_rr; + +/* Accessor macros - this is part of the public interface. */ +#define ns_rr_name(rr) (((rr).name[0] != '\0') ? (rr).name : ".") +#define ns_rr_type(rr) ((ns_type)((rr).type + 0)) +#define ns_rr_class(rr) ((ns_class)((rr).rr_class + 0)) +#define ns_rr_ttl(rr) ((rr).ttl + 0) +#define ns_rr_rdlen(rr) ((rr).rdlength + 0) +#define ns_rr_rdata(rr) ((rr).rdata + 0) + +/* + * These don't have to be in the same order as in the packet flags word, + * and they can even overlap in some cases, but they will need to be kept + * in synch with ns_parse.c:ns_flagdata[]. + */ +#define ns_flag res_9_ns_flag +typedef enum __ns_flag { + ns_f_qr, /* Question/Response. */ + ns_f_opcode, /* Operation code. */ + ns_f_aa, /* Authoritative Answer. */ + ns_f_tc, /* Truncation occurred. */ + ns_f_rd, /* Recursion Desired. */ + ns_f_ra, /* Recursion Available. */ + ns_f_z, /* MBZ. */ + ns_f_ad, /* Authentic Data (DNSSEC). */ + ns_f_cd, /* Checking Disabled (DNSSEC). */ + ns_f_rcode, /* Response code. */ + ns_f_max +} ns_flag; + +/* + * Currently defined opcodes. + */ +#define ns_opcode res_9_ns_opcode +typedef enum __ns_opcode { + ns_o_query = 0, /* Standard query. */ + ns_o_iquery = 1, /* Inverse query (deprecated/unsupported). */ + ns_o_status = 2, /* Name server status query (unsupported). */ + /* Opcode 3 is undefined/reserved. */ + ns_o_notify = 4, /* Zone change notification. */ + ns_o_update = 5, /* Zone update message. */ + ns_o_max = 6 +} ns_opcode; + +/* + * Currently defined response codes. + */ +#define ns_rcode res_9_ns_rcode +typedef enum __ns_rcode { + ns_r_noerror = 0, /* No error occurred. */ + ns_r_formerr = 1, /* Format error. */ + ns_r_servfail = 2, /* Server failure. */ + ns_r_nxdomain = 3, /* Name error. */ + ns_r_notimpl = 4, /* Unimplemented. */ + ns_r_refused = 5, /* Operation refused. */ + /* these are for BIND_UPDATE */ + ns_r_yxdomain = 6, /* Name exists */ + ns_r_yxrrset = 7, /* RRset exists */ + ns_r_nxrrset = 8, /* RRset does not exist */ + ns_r_notauth = 9, /* Not authoritative for zone */ + ns_r_notzone = 10, /* Zone of record different from zone section */ + ns_r_max = 11, + /* The following are EDNS extended rcodes */ + ns_r_badvers = 16, + /* The following are TSIG errors */ + ns_r_badsig = 16, + ns_r_badkey = 17, + ns_r_badtime = 18 +} ns_rcode; + +/* BIND_UPDATE */ +#define ns_update_operation res_9_ns_update_operation +typedef enum __ns_update_operation { + ns_uop_delete = 0, + ns_uop_add = 1, + ns_uop_max = 2 +} ns_update_operation; + +/* + * This structure is used for TSIG authenticated messages + */ +#define ns_tsig_key res_9_ns_tsig_key +struct ns_tsig_key { + char name[NS_MAXDNAME], alg[NS_MAXDNAME]; + unsigned char *data; + int len; +}; +typedef struct ns_tsig_key ns_tsig_key; + +/* + * This structure is used for TSIG authenticated TCP messages + */ +#define ns_tcp_tsig_state res_9_ns_tcp_tsig_state +struct ns_tcp_tsig_state { + int counter; + struct dst_key *key; + void *ctx; + unsigned char sig[NS_PACKETSZ]; + int siglen; +}; +typedef struct ns_tcp_tsig_state ns_tcp_tsig_state; + +#define NS_TSIG_FUDGE 300 +#define NS_TSIG_TCP_COUNT 100 +#define NS_TSIG_ALG_HMAC_MD5 "HMAC-MD5.SIG-ALG.REG.INT" + +#define NS_TSIG_ERROR_NO_TSIG -10 +#define NS_TSIG_ERROR_NO_SPACE -11 +#define NS_TSIG_ERROR_FORMERR -12 + +/* + * Currently defined type values for resources and queries. + */ +#define ns_typw res_9_ns_type +typedef enum __ns_type { + ns_t_invalid = 0, /* Cookie. */ + ns_t_a = 1, /* Host address. */ + ns_t_ns = 2, /* Authoritative server. */ + ns_t_md = 3, /* Mail destination. */ + ns_t_mf = 4, /* Mail forwarder. */ + ns_t_cname = 5, /* Canonical name. */ + ns_t_soa = 6, /* Start of authority zone. */ + ns_t_mb = 7, /* Mailbox domain name. */ + ns_t_mg = 8, /* Mail group member. */ + ns_t_mr = 9, /* Mail rename name. */ + ns_t_null = 10, /* Null resource record. */ + ns_t_wks = 11, /* Well known service. */ + ns_t_ptr = 12, /* Domain name pointer. */ + ns_t_hinfo = 13, /* Host information. */ + ns_t_minfo = 14, /* Mailbox information. */ + ns_t_mx = 15, /* Mail routing information. */ + ns_t_txt = 16, /* Text strings. */ + ns_t_rp = 17, /* Responsible person. */ + ns_t_afsdb = 18, /* AFS cell database. */ + ns_t_x25 = 19, /* X_25 calling address. */ + ns_t_isdn = 20, /* ISDN calling address. */ + ns_t_rt = 21, /* Router. */ + ns_t_nsap = 22, /* NSAP address. */ + ns_t_nsap_ptr = 23, /* Reverse NSAP lookup (deprecated). */ + ns_t_sig = 24, /* Security signature. */ + ns_t_key = 25, /* Security key. */ + ns_t_px = 26, /* X.400 mail mapping. */ + ns_t_gpos = 27, /* Geographical position (withdrawn). */ + ns_t_aaaa = 28, /* Ip6 Address. */ + ns_t_loc = 29, /* Location Information. */ + ns_t_nxt = 30, /* Next domain (security). */ + ns_t_eid = 31, /* Endpoint identifier. */ + ns_t_nimloc = 32, /* Nimrod Locator. */ + ns_t_srv = 33, /* Server Selection. */ + ns_t_atma = 34, /* ATM Address */ + ns_t_naptr = 35, /* Naming Authority PoinTeR */ + ns_t_kx = 36, /* Key Exchange */ + ns_t_cert = 37, /* Certification record */ + ns_t_a6 = 38, /* IPv6 address (deprecates AAAA) */ + ns_t_dname = 39, /* Non-terminal DNAME (for IPv6) */ + ns_t_sink = 40, /* Kitchen sink (experimentatl) */ + ns_t_opt = 41, /* EDNS0 option (meta-RR) */ + ns_t_tkey = 249, /* Transaction key */ + ns_t_tsig = 250, /* Transaction signature. */ + ns_t_ixfr = 251, /* Incremental zone transfer. */ + ns_t_axfr = 252, /* Transfer zone of authority. */ + ns_t_mailb = 253, /* Transfer mailbox records. */ + ns_t_maila = 254, /* Transfer mail agent records. */ + ns_t_any = 255, /* Wildcard match. */ + ns_t_zxfr = 256, /* BIND-specific, nonstandard. */ + ns_t_max = 65536 +} ns_type; + +/* Exclusively a QTYPE? (not also an RTYPE) */ +#define ns_t_qt_p(t) (ns_t_xfr_p(t) || (t) == ns_t_any || \ + (t) == ns_t_mailb || (t) == ns_t_maila) +/* Some kind of meta-RR? (not a QTYPE, but also not an RTYPE) */ +#define ns_t_mrr_p(t) ((t) == ns_t_tsig || (t) == ns_t_opt) +/* Exclusively an RTYPE? (not also a QTYPE or a meta-RR) */ +#define ns_t_rr_p(t) (!ns_t_qt_p(t) && !ns_t_mrr_p(t)) +#define ns_t_udp_p(t) ((t) != ns_t_axfr && (t) != ns_t_zxfr) +#define ns_t_xfr_p(t) ((t) == ns_t_axfr || (t) == ns_t_ixfr || \ + (t) == ns_t_zxfr) + +/* + * Values for class field + */ +#define ns_class res_9_ns_class +typedef enum __ns_class { + ns_c_invalid = 0, /* Cookie. */ + ns_c_in = 1, /* Internet. */ + ns_c_2 = 2, /* unallocated/unsupported. */ + ns_c_chaos = 3, /* MIT Chaos-net. */ + ns_c_hs = 4, /* MIT Hesiod. */ + /* Query class values which do not appear in resource records */ + ns_c_none = 254, /* for prereq. sections in update requests */ + ns_c_any = 255, /* Wildcard match. */ + ns_c_max = 65536 +} ns_class; + +/* DNSSEC constants. */ + +#define ns_key_types res_9_ns_key_types +typedef enum __ns_key_types { + ns_kt_rsa = 1, /* key type RSA/MD5 */ + ns_kt_dh = 2, /* Diffie Hellman */ + ns_kt_dsa = 3, /* Digital Signature Standard (MANDATORY) */ + ns_kt_private = 254 /* Private key type starts with OID */ +} ns_key_types; + +#define ns_cert_types res_9_ns_cert_types +typedef enum __ns_cert_types { + cert_t_pkix = 1, /* PKIX (X.509v3) */ + cert_t_spki = 2, /* SPKI */ + cert_t_pgp = 3, /* PGP */ + cert_t_url = 253, /* URL private type */ + cert_t_oid = 254 /* OID private type */ +} ns_cert_types; + +/* Flags field of the KEY RR rdata. */ +#define NS_KEY_TYPEMASK 0xC000 /* Mask for "type" bits */ +#define NS_KEY_TYPE_AUTH_CONF 0x0000 /* Key usable for both */ +#define NS_KEY_TYPE_CONF_ONLY 0x8000 /* Key usable for confidentiality */ +#define NS_KEY_TYPE_AUTH_ONLY 0x4000 /* Key usable for authentication */ +#define NS_KEY_TYPE_NO_KEY 0xC000 /* No key usable for either; no key */ +/* The type bits can also be interpreted independently, as single bits: */ +#define NS_KEY_NO_AUTH 0x8000 /* Key unusable for authentication */ +#define NS_KEY_NO_CONF 0x4000 /* Key unusable for confidentiality */ +#define NS_KEY_RESERVED2 0x2000 /* Security is *mandatory* if bit=0 */ +#define NS_KEY_EXTENDED_FLAGS 0x1000 /* reserved - must be zero */ +#define NS_KEY_RESERVED4 0x0800 /* reserved - must be zero */ +#define NS_KEY_RESERVED5 0x0400 /* reserved - must be zero */ +#define NS_KEY_NAME_TYPE 0x0300 /* these bits determine the type */ +#define NS_KEY_NAME_USER 0x0000 /* key is assoc. with user */ +#define NS_KEY_NAME_ENTITY 0x0200 /* key is assoc. with entity eg host */ +#define NS_KEY_NAME_ZONE 0x0100 /* key is zone key */ +#define NS_KEY_NAME_RESERVED 0x0300 /* reserved meaning */ +#define NS_KEY_RESERVED8 0x0080 /* reserved - must be zero */ +#define NS_KEY_RESERVED9 0x0040 /* reserved - must be zero */ +#define NS_KEY_RESERVED10 0x0020 /* reserved - must be zero */ +#define NS_KEY_RESERVED11 0x0010 /* reserved - must be zero */ +#define NS_KEY_SIGNATORYMASK 0x000F /* key can sign RR's of same name */ +#define NS_KEY_RESERVED_BITMASK ( NS_KEY_RESERVED2 | \ + NS_KEY_RESERVED4 | \ + NS_KEY_RESERVED5 | \ + NS_KEY_RESERVED8 | \ + NS_KEY_RESERVED9 | \ + NS_KEY_RESERVED10 | \ + NS_KEY_RESERVED11 ) +#define NS_KEY_RESERVED_BITMASK2 0xFFFF /* no bits defined here */ + +/* The Algorithm field of the KEY and SIG RR's is an integer, {1..254} */ +#define NS_ALG_MD5RSA 1 /* MD5 with RSA */ +#define NS_ALG_DH 2 /* Diffie Hellman KEY */ +#define NS_ALG_DSA 3 /* DSA KEY */ +#define NS_ALG_DSS NS_ALG_DSA +#define NS_ALG_EXPIRE_ONLY 253 /* No alg, no security */ +#define NS_ALG_PRIVATE_OID 254 /* Key begins with OID giving alg */ + +/* Protocol values */ +/* value 0 is reserved */ +#define NS_KEY_PROT_TLS 1 +#define NS_KEY_PROT_EMAIL 2 +#define NS_KEY_PROT_DNSSEC 3 +#define NS_KEY_PROT_IPSEC 4 +#define NS_KEY_PROT_ANY 255 + +/* Signatures */ +#define NS_MD5RSA_MIN_BITS 512 /* Size of a mod or exp in bits */ +#define NS_MD5RSA_MAX_BITS 2552 + /* Total of binary mod and exp */ +#define NS_MD5RSA_MAX_BYTES ((NS_MD5RSA_MAX_BITS+7/8)*2+3) + /* Max length of text sig block */ +#define NS_MD5RSA_MAX_BASE64 (((NS_MD5RSA_MAX_BYTES+2)/3)*4) +#define NS_MD5RSA_MIN_SIZE ((NS_MD5RSA_MIN_BITS+7)/8) +#define NS_MD5RSA_MAX_SIZE ((NS_MD5RSA_MAX_BITS+7)/8) + +#define NS_DSA_SIG_SIZE 41 +#define NS_DSA_MIN_SIZE 213 +#define NS_DSA_MAX_BYTES 405 + +/* Offsets into SIG record rdata to find various values */ +#define NS_SIG_TYPE 0 /* Type flags */ +#define NS_SIG_ALG 2 /* Algorithm */ +#define NS_SIG_LABELS 3 /* How many labels in name */ +#define NS_SIG_OTTL 4 /* Original TTL */ +#define NS_SIG_EXPIR 8 /* Expiration time */ +#define NS_SIG_SIGNED 12 /* Signature time */ +#define NS_SIG_FOOT 16 /* Key footprint */ +#define NS_SIG_SIGNER 18 /* Domain name of who signed it */ + +/* How RR types are represented as bit-flags in NXT records */ +#define NS_NXT_BITS 8 +#define NS_NXT_BIT_SET( n,p) (p[(n)/NS_NXT_BITS] |= (0x80>>((n)%NS_NXT_BITS))) +#define NS_NXT_BIT_CLEAR(n,p) (p[(n)/NS_NXT_BITS] &= ~(0x80>>((n)%NS_NXT_BITS))) +#define NS_NXT_BIT_ISSET(n,p) (p[(n)/NS_NXT_BITS] & (0x80>>((n)%NS_NXT_BITS))) +#define NS_NXT_MAX 127 + +/* + * EDNS0 extended flags, host order. + */ +#define NS_OPT_DNSSEC_OK 0x8000U + +/* + * Inline versions of get/put short/long. Pointer is advanced. + */ +#define NS_GET16(s, cp) do { \ + register const u_char *t_cp = (const u_char *)(cp); \ + (s) = ((u_int16_t)t_cp[0] << 8) \ + | ((u_int16_t)t_cp[1]) \ + ; \ + (cp) += NS_INT16SZ; \ +} while (0) + +#define NS_GET32(l, cp) do { \ + register const u_char *t_cp = (const u_char *)(cp); \ + (l) = ((u_int32_t)t_cp[0] << 24) \ + | ((u_int32_t)t_cp[1] << 16) \ + | ((u_int32_t)t_cp[2] << 8) \ + | ((u_int32_t)t_cp[3]) \ + ; \ + (cp) += NS_INT32SZ; \ +} while (0) + +#define NS_PUT16(s, cp) do { \ + register u_int16_t t_s = (u_int16_t)(s); \ + register u_char *t_cp = (u_char *)(cp); \ + *t_cp++ = t_s >> 8; \ + *t_cp = t_s; \ + (cp) += NS_INT16SZ; \ +} while (0) + +#define NS_PUT32(l, cp) do { \ + register u_int32_t t_l = (u_int32_t)(l); \ + register u_char *t_cp = (u_char *)(cp); \ + *t_cp++ = t_l >> 24; \ + *t_cp++ = t_l >> 16; \ + *t_cp++ = t_l >> 8; \ + *t_cp = t_l; \ + (cp) += NS_INT32SZ; \ +} while (0) + +/* + * ANSI C identifier hiding for bind's lib/nameser. + */ +#define ns_msg_getflag res_9_ns_msg_getflag +#define ns_get16 res_9_ns_get16 +#define ns_get32 res_9_ns_get32 +#define ns_put16 res_9_ns_put16 +#define ns_put32 res_9_ns_put32 +#define ns_initparse res_9_ns_initparse +#define ns_skiprr res_9_ns_skiprr +#define ns_parserr res_9_ns_parserr +#define ns_sprintrr res_9_ns_sprintrr +#define ns_sprintrrf res_9_ns_sprintrrf +#define ns_format_ttl res_9_ns_format_ttl +#define ns_parse_ttl res_9_ns_parse_ttl +#define ns_datetosecs res_9_ns_datetosecs +#define ns_name_ntol res_9_ns_name_ntol +#define ns_name_ntop res_9_ns_name_ntop +#define ns_name_pton res_9_ns_name_pton +#define ns_name_unpack res_9_ns_name_unpack +#define ns_name_pack res_9_ns_name_pack +#define ns_name_compress res_9_ns_name_compress +#define ns_name_uncompress res_9_ns_name_uncompress +#define ns_name_skip res_9_ns_name_skip +#define ns_name_rollback res_9_ns_name_rollback +#define ns_sign res_9_ns_sign +#define ns_sign2 res_9_ns_sign2 +#define ns_sign_tcp res_9_ns_sign_tcp +#define ns_sign_tcp2 res_9_ns_sign_tcp2 +#define ns_sign_tcp_init res_9_ns_sign_tcp_init +#define ns_find_tsig res_9_ns_find_tsig +#define ns_verify res_9_ns_verify +#define ns_verify_tcp res_9_ns_verify_tcp +#define ns_verify_tcp_init res_9_ns_verify_tcp_init +#define ns_samedomain res_9_ns_samedomain +#define ns_subdomain res_9_ns_subdomain +#define ns_makecanon res_9_ns_makecanon +#define ns_samename res_9_ns_samename + +__BEGIN_DECLS +int ns_msg_getflag __P((ns_msg, int)); +u_int ns_get16 __P((const u_char *)); +u_long ns_get32 __P((const u_char *)); +void ns_put16 __P((u_int, u_char *)); +void ns_put32 __P((u_long, u_char *)); +int ns_initparse __P((const u_char *, int, ns_msg *)); +int ns_skiprr __P((const u_char *, const u_char *, ns_sect, int)); +int ns_parserr __P((ns_msg *, ns_sect, int, ns_rr *)); +int ns_sprintrr __P((const ns_msg *, const ns_rr *, + const char *, const char *, char *, size_t)); +int ns_sprintrrf __P((const u_char *, size_t, const char *, + ns_class, ns_type, u_long, const u_char *, + size_t, const char *, const char *, + char *, size_t)); +int ns_format_ttl __P((u_long, char *, size_t)); +int ns_parse_ttl __P((const char *, u_long *)); +u_int32_t ns_datetosecs __P((const char *cp, int *errp)); +int ns_name_ntol __P((const u_char *, u_char *, size_t)); +int ns_name_ntop __P((const u_char *, char *, size_t)); +int ns_name_pton __P((const char *, u_char *, size_t)); +int ns_name_unpack __P((const u_char *, const u_char *, + const u_char *, u_char *, size_t)); +int ns_name_pack __P((const u_char *, u_char *, int, + const u_char **, const u_char **)); +int ns_name_uncompress __P((const u_char *, const u_char *, + const u_char *, char *, size_t)); +int ns_name_compress __P((const char *, u_char *, size_t, + const u_char **, const u_char **)); +int ns_name_skip __P((const u_char **, const u_char *)); +void ns_name_rollback __P((const u_char *, const u_char **, + const u_char **)); +int ns_sign __P((u_char *, int *, int, int, void *, + const u_char *, int, u_char *, int *, time_t)); +int ns_sign2 __P((u_char *, int *, int, int, void *, + const u_char *, int, u_char *, int *, time_t, + u_char **, u_char **)); +int ns_sign_tcp __P((u_char *, int *, int, int, + ns_tcp_tsig_state *, int)); +int ns_sign_tcp2 __P((u_char *, int *, int, int, + ns_tcp_tsig_state *, int, + u_char **, u_char **)); +int ns_sign_tcp_init __P((void *, const u_char *, int, + ns_tcp_tsig_state *)); +u_char *ns_find_tsig __P((u_char *, u_char *)); +int ns_verify __P((u_char *, int *, void *, + const u_char *, int, u_char *, int *, + time_t *, int)); +int ns_verify_tcp __P((u_char *, int *, ns_tcp_tsig_state *, int)); +int ns_verify_tcp_init __P((void *, const u_char *, int, + ns_tcp_tsig_state *)); +int ns_samedomain __P((const char *, const char *)); +int ns_subdomain __P((const char *, const char *)); +int ns_makecanon __P((const char *, char *, size_t)); +int ns_samename __P((const char *, const char *)); +__END_DECLS + +#endif /* !BIND_8_COMPAT */ +#endif /* !_NAMESER_9_H_ */ diff --git a/ns_date.c b/ns_date.c new file mode 100644 index 0000000..89ed7d1 --- /dev/null +++ b/ns_date.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#ifndef __APPLE__ +#ifndef lint +static const char rcsid[] = "$Id: ns_date.c,v 1.1 2006/03/01 19:01:36 majka Exp $"; +#endif +#endif + +/* Import. */ + +#ifndef __APPLE__ +#include "port_before.h" +#endif + +#include + +#include +#include +#include +#include +#include + +#ifndef __APPLE__ +#include "port_after.h" +#endif + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/* Forward. */ + +static int datepart(const char *, int, int, int, int *); + +/* Public. */ + +/* Convert a date in ASCII into the number of seconds since + 1 January 1970 (GMT assumed). Format is yyyymmddhhmmss, all + digits required, no spaces allowed. */ + +u_int32_t +ns_datetosecs(const char *cp, int *errp) { + struct tm time; + u_int32_t result; + int mdays, i; + static const int days_per_month[12] = + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + if (strlen(cp) != 14) { + *errp = 1; + return (0); + } + *errp = 0; + + memset(&time, 0, sizeof time); + time.tm_year = datepart(cp + 0, 4, 1990, 9999, errp) - 1900; + time.tm_mon = datepart(cp + 4, 2, 01, 12, errp) - 1; + time.tm_mday = datepart(cp + 6, 2, 01, 31, errp); + time.tm_hour = datepart(cp + 8, 2, 00, 23, errp); + time.tm_min = datepart(cp + 10, 2, 00, 59, errp); + time.tm_sec = datepart(cp + 12, 2, 00, 59, errp); + if (*errp) /* Any parse errors? */ + return (0); + + /* + * OK, now because timegm() is not available in all environments, + * we will do it by hand. Roll up sleeves, curse the gods, begin! + */ + +#define SECS_PER_DAY ((u_int32_t)24*60*60) +#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) + + result = time.tm_sec; /* Seconds */ + result += time.tm_min * 60; /* Minutes */ + result += time.tm_hour * (60*60); /* Hours */ + result += (time.tm_mday - 1) * SECS_PER_DAY; /* Days */ + + /* Months are trickier. Look without leaping, then leap */ + mdays = 0; + for (i = 0; i < time.tm_mon; i++) + mdays += days_per_month[i]; + result += mdays * SECS_PER_DAY; /* Months */ + if (time.tm_mon > 1 && isleap(1900+time.tm_year)) + result += SECS_PER_DAY; /* Add leapday for this year */ + + /* First figure years without leapdays, then add them in. */ + /* The loop is slow, FIXME, but simple and accurate. */ + result += (time.tm_year - 70) * (SECS_PER_DAY*365); /* Years */ + for (i = 70; i < time.tm_year; i++) + if (isleap(1900+i)) + result += SECS_PER_DAY; /* Add leapday for prev year */ + + return (result); +} + +/* Private. */ + +/* + * Parse part of a date. Set error flag if any error. + * Don't reset the flag if there is no error. + */ +static int +datepart(const char *buf, int size, int min, int max, int *errp) { + int result = 0; + int i; + + for (i = 0; i < size; i++) { + if (!isdigit((unsigned char)(buf[i]))) + *errp = 1; + result = (result * 10) + buf[i] - '0'; + } + if (result < min) + *errp = 1; + if (result > max) + *errp = 1; + return (result); +} diff --git a/ns_name.c b/ns_name.c new file mode 100644 index 0000000..07b0739 --- /dev/null +++ b/ns_name.c @@ -0,0 +1,949 @@ +/* + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#ifndef __APPLE__ +#ifndef lint +static const char rcsid[] = "$Id: ns_name.c,v 1.1 2006/03/01 19:01:36 majka Exp $"; +#endif +#endif + +#ifndef __APPLE__ +#include "port_before.h" +#endif + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef __APPLE__ +#include "port_after.h" +#endif + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +#define NS_TYPE_ELT 0x40 /* EDNS0 extended label type */ +#define DNS_LABELTYPE_BITSTRING 0x41 + +/* Data. */ + +static const char digits[] = "0123456789"; + +static const char digitvalue[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/ + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/ + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/ +}; + +/* Forward. */ + +static int special(int); +static int printable(int); +static int dn_find(const u_char *, const u_char *, + const u_char * const *, + const u_char * const *); +static int encode_bitsring(const char **, const char *, + char **, char **, const char *); +static int labellen(const u_char *); +static int decode_bitstring(const char **, char *, const char *); + +/* Public. */ + +/* + * ns_name_ntop(src, dst, dstsiz) + * Convert an encoded domain name to printable ascii as per RFC1035. + * return: + * Number of bytes written to buffer, or -1 (with errno set) + * notes: + * The root is returned as "." + * All other domains are returned in non absolute form + */ +int +ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) +{ + const u_char *cp; + char *dn, *eom; + u_char c; + u_int n; + int l; + + cp = src; + dn = dst; + eom = dst + dstsiz; + + while ((n = *cp++) != 0) { + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* Some kind of compression pointer. */ + errno = EMSGSIZE; + return (-1); + } + if (dn != dst) { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '.'; + } + if ((l = labellen(cp - 1)) < 0) { + errno = EMSGSIZE; /* XXX */ + return(-1); + } + if (dn + l >= eom) { + errno = EMSGSIZE; + return (-1); + } + if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) { + int m; + + if (n != DNS_LABELTYPE_BITSTRING) { + /* XXX: labellen should reject this case */ + errno = EINVAL; + return(-1); + } + if ((m = decode_bitstring((const char **)&cp, dn, eom)) < 0) + { + errno = EMSGSIZE; + return(-1); + } + dn += m; + continue; + } + for ((void)NULL; l > 0; l--) { + c = *cp++; + if (special(c)) { + if (dn + 1 >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\\'; + *dn++ = (char)c; + } else if (!printable(c)) { + if (dn + 3 >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\\'; + *dn++ = digits[c / 100]; + *dn++ = digits[(c % 100) / 10]; + *dn++ = digits[c % 10]; + } else { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = (char)c; + } + } + } + if (dn == dst) { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '.'; + } + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\0'; + return (dn - dst); +} + +/* + * ns_name_pton(src, dst, dstsiz) + * Convert a ascii string into an encoded domain name as per RFC1035. + * return: + * -1 if it fails + * 1 if string was fully qualified + * 0 is string was not fully qualified + * notes: + * Enforces label and domain length limits. + */ + +int +ns_name_pton(const char *src, u_char *dst, size_t dstsiz) +{ + u_char *label, *bp, *eom; + int c, n, escaped, e = 0; + char *cp; + + escaped = 0; + bp = dst; + eom = dst + dstsiz; + label = bp++; + + while ((c = *src++) != 0) { + if (escaped) { + if (c == '[') { /* start a bit string label */ + if ((cp = strchr(src, ']')) == NULL) { + errno = EINVAL; /* ??? */ + return(-1); + } + if ((e = encode_bitsring(&src, + cp + 2, + (char **)&label, + (char **)&bp, + (const char *)eom)) + != 0) { + errno = e; + return(-1); + } + escaped = 0; + label = bp++; + if ((c = *src++) == 0) + goto done; + else if (c != '.') { + errno = EINVAL; + return(-1); + } + continue; + } + else if ((cp = strchr(digits, c)) != NULL) { + n = (cp - digits) * 100; + if ((c = *src++) == 0 || + (cp = strchr(digits, c)) == NULL) { + errno = EMSGSIZE; + return (-1); + } + n += (cp - digits) * 10; + if ((c = *src++) == 0 || + (cp = strchr(digits, c)) == NULL) { + errno = EMSGSIZE; + return (-1); + } + n += (cp - digits); + if (n > 255) { + errno = EMSGSIZE; + return (-1); + } + c = n; + } + escaped = 0; + } else if (c == '\\') { + escaped = 1; + continue; + } else if (c == '.') { + c = (bp - label - 1); + if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ + errno = EMSGSIZE; + return (-1); + } + if (label >= eom) { + errno = EMSGSIZE; + return (-1); + } + *label = c; + /* Fully qualified ? */ + if (*src == '\0') { + if (c != 0) { + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = '\0'; + } + if ((bp - dst) > NS_MAXCDNAME) { + errno = EMSGSIZE; + return (-1); + } + return (1); + } + if (c == 0 || *src == '.') { + errno = EMSGSIZE; + return (-1); + } + label = bp++; + continue; + } + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = (u_char)c; + } + c = (bp - label - 1); + if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ + errno = EMSGSIZE; + return (-1); + } + done: + if (label >= eom) { + errno = EMSGSIZE; + return (-1); + } + *label = c; + if (c != 0) { + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = 0; + } + if ((bp - dst) > NS_MAXCDNAME) { /* src too big */ + errno = EMSGSIZE; + return (-1); + } + return (0); +} + +/* + * ns_name_ntol(src, dst, dstsiz) + * Convert a network strings labels into all lowercase. + * return: + * Number of bytes written to buffer, or -1 (with errno set) + * notes: + * Enforces label and domain length limits. + */ + +int +ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz) +{ + const u_char *cp; + u_char *dn, *eom; + u_char c; + u_int n; + int l; + + cp = src; + dn = dst; + eom = dst + dstsiz; + + while ((n = *cp++) != 0) { + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* Some kind of compression pointer. */ + errno = EMSGSIZE; + return (-1); + } + *dn++ = n; + if ((l = labellen(cp - 1)) < 0) { + errno = EMSGSIZE; + return (-1); + } + if (dn + l >= eom) { + errno = EMSGSIZE; + return (-1); + } + for ((void)NULL; l > 0; l--) { + c = *cp++; + if (isupper(c)) + *dn++ = tolower(c); + else + *dn++ = c; + } + } + *dn++ = '\0'; + return (dn - dst); +} + +/* + * ns_name_unpack(msg, eom, src, dst, dstsiz) + * Unpack a domain name from a message, source may be compressed. + * return: + * -1 if it fails, or consumed octets if it succeeds. + */ +int +ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src, + u_char *dst, size_t dstsiz) +{ + const u_char *srcp, *dstlim; + u_char *dstp; + int n, len, checked, l; + + len = -1; + checked = 0; + dstp = dst; + srcp = src; + dstlim = dst + dstsiz; + if (srcp < msg || srcp >= eom) { + errno = EMSGSIZE; + return (-1); + } + /* Fetch next label in domain name. */ + while ((n = *srcp++) != 0) { + /* Check for indirection. */ + switch (n & NS_CMPRSFLGS) { + case 0: + case NS_TYPE_ELT: + /* Limit checks. */ + if ((l = labellen(srcp - 1)) < 0) { + errno = EMSGSIZE; + return(-1); + } + if (dstp + l + 1 >= dstlim || srcp + l >= eom) { + errno = EMSGSIZE; + return (-1); + } + checked += l + 1; + *dstp++ = n; + memcpy(dstp, srcp, l); + dstp += l; + srcp += l; + break; + + case NS_CMPRSFLGS: + if (srcp >= eom) { + errno = EMSGSIZE; + return (-1); + } + if (len < 0) + len = srcp - src + 1; + srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff)); + if (srcp < msg || srcp >= eom) { /* Out of range. */ + errno = EMSGSIZE; + return (-1); + } + checked += 2; + /* + * Check for loops in the compressed name; + * if we've looked at the whole message, + * there must be a loop. + */ + if (checked >= eom - msg) { + errno = EMSGSIZE; + return (-1); + } + break; + + default: + errno = EMSGSIZE; + return (-1); /* flag error */ + } + } + *dstp = '\0'; + if (len < 0) + len = srcp - src; + return (len); +} + +/* + * ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr) + * Pack domain name 'domain' into 'comp_dn'. + * return: + * Size of the compressed name, or -1. + * notes: + * 'dnptrs' is an array of pointers to previous compressed names. + * dnptrs[0] is a pointer to the beginning of the message. The array + * ends with NULL. + * 'lastdnptr' is a pointer to the end of the array pointed to + * by 'dnptrs'. + * Side effects: + * The list of pointers in dnptrs is updated for labels inserted into + * the message as we compress the name. If 'dnptr' is NULL, we don't + * try to compress names. If 'lastdnptr' is NULL, we don't update the + * list. + */ +int +ns_name_pack(const u_char *src, u_char *dst, int dstsiz, + const u_char **dnptrs, const u_char **lastdnptr) +{ + u_char *dstp; + const u_char **cpp, **lpp, *eob, *msg; + const u_char *srcp; + int n, l, first = 1; + + srcp = src; + dstp = dst; + eob = dstp + dstsiz; + lpp = cpp = NULL; + if (dnptrs != NULL) { + if ((msg = *dnptrs++) != NULL) { + for (cpp = dnptrs; *cpp != NULL; cpp++) + (void)NULL; + lpp = cpp; /* end of list to search */ + } + } else + msg = NULL; + + /* make sure the domain we are about to add is legal */ + l = 0; + do { + int l0; + + n = *srcp; + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + errno = EMSGSIZE; + return (-1); + } + if ((l0 = labellen(srcp)) < 0) { + errno = EINVAL; + return(-1); + } + l += l0 + 1; + if (l > NS_MAXCDNAME) { + errno = EMSGSIZE; + return (-1); + } + srcp += l0 + 1; + } while (n != 0); + + /* from here on we need to reset compression pointer array on error */ + srcp = src; + do { + /* Look to see if we can use pointers. */ + n = *srcp; + if (n != 0 && msg != NULL) { + l = dn_find(srcp, msg, (const u_char * const *)dnptrs, + (const u_char * const *)lpp); + if (l >= 0) { + if (dstp + 1 >= eob) { + goto cleanup; + } + *dstp++ = (l >> 8) | NS_CMPRSFLGS; + *dstp++ = l % 256; + return (dstp - dst); + } + /* Not found, save it. */ + if (lastdnptr != NULL && cpp < lastdnptr - 1 && + (dstp - msg) < 0x4000 && first) { + *cpp++ = dstp; + *cpp = NULL; + first = 0; + } + } + /* copy label to buffer */ + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* Should not happen. */ + goto cleanup; + } + n = labellen(srcp); + if (dstp + 1 + n >= eob) { + goto cleanup; + } + memcpy(dstp, srcp, n + 1); + srcp += n + 1; + dstp += n + 1; + } while (n != 0); + + if (dstp > eob) { +cleanup: + if (msg != NULL) + *lpp = NULL; + errno = EMSGSIZE; + return (-1); + } + return (dstp - dst); +} + +/* + * ns_name_uncompress(msg, eom, src, dst, dstsiz) + * Expand compressed domain name to presentation format. + * return: + * Number of bytes read out of `src', or -1 (with errno set). + * note: + * Root domain returns as "." not "". + */ +int +ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src, + char *dst, size_t dstsiz) +{ + u_char tmp[NS_MAXCDNAME]; + int n; + + if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1) + return (-1); + if (ns_name_ntop(tmp, dst, dstsiz) == -1) + return (-1); + return (n); +} + +/* + * ns_name_compress(src, dst, dstsiz, dnptrs, lastdnptr) + * Compress a domain name into wire format, using compression pointers. + * return: + * Number of bytes consumed in `dst' or -1 (with errno set). + * notes: + * 'dnptrs' is an array of pointers to previous compressed names. + * dnptrs[0] is a pointer to the beginning of the message. + * The list ends with NULL. 'lastdnptr' is a pointer to the end of the + * array pointed to by 'dnptrs'. Side effect is to update the list of + * pointers for labels inserted into the message as we compress the name. + * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' + * is NULL, we don't update the list. + */ +int +ns_name_compress(const char *src, u_char *dst, size_t dstsiz, + const u_char **dnptrs, const u_char **lastdnptr) +{ + u_char tmp[NS_MAXCDNAME]; + + if (ns_name_pton(src, tmp, sizeof tmp) == -1) + return (-1); + return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr)); +} + +/* + * Reset dnptrs so that there are no active references to pointers at or + * after src. + */ +void +ns_name_rollback(const u_char *src, const u_char **dnptrs, + const u_char **lastdnptr) +{ + while (dnptrs < lastdnptr && *dnptrs != NULL) { + if (*dnptrs >= src) { + *dnptrs = NULL; + break; + } + dnptrs++; + } +} + +/* + * ns_name_skip(ptrptr, eom) + * Advance *ptrptr to skip over the compressed name it points at. + * return: + * 0 on success, -1 (with errno set) on failure. + */ +int +ns_name_skip(const u_char **ptrptr, const u_char *eom) +{ + const u_char *cp; + u_int n; + int l; + + cp = *ptrptr; + while (cp < eom && (n = *cp++) != 0) { + /* Check for indirection. */ + switch (n & NS_CMPRSFLGS) { + case 0: /* normal case, n == len */ + cp += n; + continue; + case NS_TYPE_ELT: /* EDNS0 extended label */ + if ((l = labellen(cp - 1)) < 0) { + errno = EMSGSIZE; /* XXX */ + return(-1); + } + cp += l; + continue; + case NS_CMPRSFLGS: /* indirection */ + cp++; + break; + default: /* illegal type */ + errno = EMSGSIZE; + return (-1); + } + break; + } + if (cp > eom) { + errno = EMSGSIZE; + return (-1); + } + *ptrptr = cp; + return (0); +} + +/* Private. */ + +/* + * special(ch) + * Thinking in noninternationalized USASCII (per the DNS spec), + * is this characted special ("in need of quoting") ? + * return: + * boolean. + */ +static int +special(int ch) { + switch (ch) { + case 0x22: /* '"' */ + case 0x2E: /* '.' */ + case 0x3B: /* ';' */ + case 0x5C: /* '\\' */ + case 0x28: /* '(' */ + case 0x29: /* ')' */ + /* Special modifiers in zone files. */ + case 0x40: /* '@' */ + case 0x24: /* '$' */ + return (1); + default: + return (0); + } +} + +/* + * printable(ch) + * Thinking in noninternationalized USASCII (per the DNS spec), + * is this character visible and not a space when printed ? + * return: + * boolean. + */ +static int +printable(int ch) { + return (ch > 0x20 && ch < 0x7f); +} + +/* + * Thinking in noninternationalized USASCII (per the DNS spec), + * convert this character to lower case if it's upper case. + */ +static int +mklower(int ch) { + if (ch >= 0x41 && ch <= 0x5A) + return (ch + 0x20); + return (ch); +} + +/* + * dn_find(domain, msg, dnptrs, lastdnptr) + * Search for the counted-label name in an array of compressed names. + * return: + * offset from msg if found, or -1. + * notes: + * dnptrs is the pointer to the first name on the list, + * not the pointer to the start of the message. + */ +static int +dn_find(const u_char *domain, const u_char *msg, + const u_char * const *dnptrs, + const u_char * const *lastdnptr) +{ + const u_char *dn, *cp, *sp; + const u_char * const *cpp; + u_int n; + + for (cpp = dnptrs; cpp < lastdnptr; cpp++) { + sp = *cpp; + /* + * terminate search on: + * root label + * compression pointer + * unusable offset + */ + while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 && + (sp - msg) < 0x4000) { + dn = domain; + cp = sp; + while ((n = *cp++) != 0) { + /* + * check for indirection + */ + switch (n & NS_CMPRSFLGS) { + case 0: /* normal case, n == len */ + n = labellen(cp - 1); /* XXX */ + + if (n != *dn++) + goto next; + + for ((void)NULL; n > 0; n--) + if (mklower(*dn++) != + mklower(*cp++)) + goto next; + /* Is next root for both ? */ + if (*dn == '\0' && *cp == '\0') + return (sp - msg); + if (*dn) + continue; + goto next; + case NS_CMPRSFLGS: /* indirection */ + cp = msg + (((n & 0x3f) << 8) | *cp); + break; + + default: /* illegal type */ + errno = EMSGSIZE; + return (-1); + } + } + next: ; + sp += *sp + 1; + } + } + errno = ENOENT; + return (-1); +} + +static int +decode_bitstring(const char **cpp, char *dn, const char *eom) +{ + const char *cp = *cpp; + char *beg = dn, tc; + int b, blen, plen; + + if ((blen = (*cp & 0xff)) == 0) + blen = 256; + plen = (blen + 3) / 4; + plen += sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1); + if (dn + plen >= eom) + return(-1); + + cp++; + dn += SPRINTF((dn, "\\[x")); + for (b = blen; b > 7; b -= 8, cp++) + dn += SPRINTF((dn, "%02x", *cp & 0xff)); + if (b > 4) { + tc = *cp++; + dn += SPRINTF((dn, "%02x", tc & (0xff << (8 - b)))); + } else if (b > 0) { + tc = *cp++; + dn += SPRINTF((dn, "%1x", + ((tc >> 4) & 0x0f) & (0x0f << (4 - b)))); + } + dn += SPRINTF((dn, "/%d]", blen)); + + *cpp = cp; + return(dn - beg); +} + +static int +encode_bitsring(const char **bp, const char *end, char **labelp, + char ** dst, const char *eom) +{ + int afterslash = 0; + const char *cp = *bp; + char *tp, c; + const char *beg_blen; + char *end_blen = NULL; + int value = 0, count = 0, tbcount = 0, blen = 0; + + beg_blen = end_blen = NULL; + + /* a bitstring must contain at least 2 characters */ + if (end - cp < 2) + return(EINVAL); + + /* XXX: currently, only hex strings are supported */ + if (*cp++ != 'x') + return(EINVAL); + if (!isxdigit((*cp) & 0xff)) /* reject '\[x/BLEN]' */ + return(EINVAL); + + for (tp = *dst + 1; cp < end && tp < eom; cp++) { + switch((c = *cp)) { + case ']': /* end of the bitstring */ + if (afterslash) { + if (beg_blen == NULL) + return(EINVAL); + blen = (int)strtol(beg_blen, &end_blen, 10); + if (*end_blen != ']') + return(EINVAL); + } + if (count) + *tp++ = ((value << 4) & 0xff); + cp++; /* skip ']' */ + goto done; + case '/': + afterslash = 1; + break; + default: + if (afterslash) { + if (!isdigit(c&0xff)) + return(EINVAL); + if (beg_blen == NULL) { + + if (c == '0') { + /* blen never begings with 0 */ + return(EINVAL); + } + beg_blen = cp; + } + } else { + if (!isxdigit(c&0xff)) + return(EINVAL); + value <<= 4; + value += digitvalue[(int)c]; + count += 4; + tbcount += 4; + if (tbcount > 256) + return(EINVAL); + if (count == 8) { + *tp++ = value; + count = 0; + } + } + break; + } + } + done: + if (cp >= end || tp >= eom) + return(EMSGSIZE); + + /* + * bit length validation: + * If a is present, the number of digits in the + * MUST be just sufficient to contain the number of bits specified + * by the . If there are insignificant bits in a final + * hexadecimal or octal digit, they MUST be zero. + * RFC 2673, Section 3.2. + */ + if (blen > 0) { + int traillen; + + if (((blen + 3) & ~3) != tbcount) + return(EINVAL); + traillen = tbcount - blen; /* between 0 and 3 */ + if (((value << (8 - traillen)) & 0xff) != 0) + return(EINVAL); + } + else + blen = tbcount; + if (blen == 256) + blen = 0; + + /* encode the type and the significant bit fields */ + **labelp = DNS_LABELTYPE_BITSTRING; + **dst = blen; + + *bp = cp; + *dst = tp; + + return(0); +} + +static int +labellen(const u_char *lp) +{ + int bitlen; + u_char l = *lp; + + if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* should be avoided by the caller */ + return(-1); + } + + if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) { + if (l == DNS_LABELTYPE_BITSTRING) { + if ((bitlen = *(lp + 1)) == 0) + bitlen = 256; + return((bitlen + 7 ) / 8 + 1); + } + return(-1); /* unknwon ELT */ + } + return(l); +} diff --git a/ns_netint.c b/ns_netint.c new file mode 100644 index 0000000..b01e08b --- /dev/null +++ b/ns_netint.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#ifndef __APPLE__ +#ifndef lint +static const char rcsid[] = "$Id: ns_netint.c,v 1.1 2006/03/01 19:01:37 majka Exp $"; +#endif +#endif + +/* Import. */ + +#ifndef __APPLE__ +#include "port_before.h" +#endif + +#include + +#ifndef __APPLE__ +#include "port_after.h" +#endif + +/* Public. */ + +u_int +ns_get16(const u_char *src) { + u_int dst; + + NS_GET16(dst, src); + return (dst); +} + +u_long +ns_get32(const u_char *src) { + u_long dst; + + NS_GET32(dst, src); + return (dst); +} + +void +ns_put16(u_int src, u_char *dst) { + NS_PUT16(src, dst); +} + +void +ns_put32(u_long src, u_char *dst) { + NS_PUT32(src, dst); +} diff --git a/ns_parse.c b/ns_parse.c new file mode 100644 index 0000000..fbd1d2c --- /dev/null +++ b/ns_parse.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#ifndef __APPLE__ +#ifndef lint +static const char rcsid[] = "$Id: ns_parse.c,v 1.1 2006/03/01 19:01:37 majka Exp $"; +#endif +#endif + +/* Import. */ + +#ifndef __APPLE__ +#include "port_before.h" +#endif + +#include + +#include +#include + +#include +#include +#include + +#ifndef __APPLE__ +#include "port_after.h" +#endif + +/* Forward. */ + +static void setsection(ns_msg *msg, ns_sect sect); + +/* Macros. */ + +#define RETERR(err) do { errno = (err); return (-1); } while (0) + +/* Public. */ + +/* These need to be in the same order as the nres.h:ns_flag enum. */ +struct _ns_flagdata _ns_flagdata[16] = { + { 0x8000, 15 }, /* qr. */ + { 0x7800, 11 }, /* opcode. */ + { 0x0400, 10 }, /* aa. */ + { 0x0200, 9 }, /* tc. */ + { 0x0100, 8 }, /* rd. */ + { 0x0080, 7 }, /* ra. */ + { 0x0040, 6 }, /* z. */ + { 0x0020, 5 }, /* ad. */ + { 0x0010, 4 }, /* cd. */ + { 0x000f, 0 }, /* rcode. */ + { 0x0000, 0 }, /* expansion (1/6). */ + { 0x0000, 0 }, /* expansion (2/6). */ + { 0x0000, 0 }, /* expansion (3/6). */ + { 0x0000, 0 }, /* expansion (4/6). */ + { 0x0000, 0 }, /* expansion (5/6). */ + { 0x0000, 0 }, /* expansion (6/6). */ +}; + +int ns_msg_getflag(ns_msg handle, int flag) { + return(((handle)._flags & _ns_flagdata[flag].mask) >> _ns_flagdata[flag].shift); +} + +int +ns_skiprr(const u_char *ptr, const u_char *eom, ns_sect section, int count) { + const u_char *optr = ptr; + + for ((void)NULL; count > 0; count--) { + int b, rdlength; + + b = dn_skipname(ptr, eom); + if (b < 0) + RETERR(EMSGSIZE); + ptr += b/*Name*/ + NS_INT16SZ/*Type*/ + NS_INT16SZ/*Class*/; + if (section != ns_s_qd) { + if (ptr + NS_INT32SZ + NS_INT16SZ > eom) + RETERR(EMSGSIZE); + ptr += NS_INT32SZ/*TTL*/; + NS_GET16(rdlength, ptr); + ptr += rdlength/*RData*/; + } + } + if (ptr > eom) + RETERR(EMSGSIZE); + return (ptr - optr); +} + +int +ns_initparse(const u_char *msg, int msglen, ns_msg *handle) { + const u_char *eom = msg + msglen; + int i; + + memset(handle, 0x5e, sizeof *handle); + handle->_msg = msg; + handle->_eom = eom; + if (msg + NS_INT16SZ > eom) + RETERR(EMSGSIZE); + NS_GET16(handle->_id, msg); + if (msg + NS_INT16SZ > eom) + RETERR(EMSGSIZE); + NS_GET16(handle->_flags, msg); + for (i = 0; i < ns_s_max; i++) { + if (msg + NS_INT16SZ > eom) + RETERR(EMSGSIZE); + NS_GET16(handle->_counts[i], msg); + } + for (i = 0; i < ns_s_max; i++) + if (handle->_counts[i] == 0) + handle->_sections[i] = NULL; + else { + int b = ns_skiprr(msg, eom, (ns_sect)i, + handle->_counts[i]); + + if (b < 0) + return (-1); + handle->_sections[i] = msg; + msg += b; + } + if (msg != eom) + RETERR(EMSGSIZE); + setsection(handle, ns_s_max); + return (0); +} + +int +ns_parserr(ns_msg *handle, ns_sect section, int rrnum, ns_rr *rr) { + int b; + + /* Make section right. */ + if (section >= ns_s_max) + RETERR(ENODEV); + if (section != handle->_sect) + setsection(handle, section); + + /* Make rrnum right. */ + if (rrnum == -1) + rrnum = handle->_rrnum; + if (rrnum < 0 || rrnum >= handle->_counts[(int)section]) + RETERR(ENODEV); + if (rrnum < handle->_rrnum) + setsection(handle, section); + if (rrnum > handle->_rrnum) { + b = ns_skiprr(handle->_msg_ptr, handle->_eom, section, + rrnum - handle->_rrnum); + + if (b < 0) + return (-1); + handle->_msg_ptr += b; + handle->_rrnum = rrnum; + } + + /* Do the parse. */ + b = dn_expand(handle->_msg, handle->_eom, + handle->_msg_ptr, rr->name, NS_MAXDNAME); + if (b < 0) + return (-1); + handle->_msg_ptr += b; + if (handle->_msg_ptr + NS_INT16SZ + NS_INT16SZ > handle->_eom) + RETERR(EMSGSIZE); + NS_GET16(rr->type, handle->_msg_ptr); + NS_GET16(rr->rr_class, handle->_msg_ptr); + if (section == ns_s_qd) { + rr->ttl = 0; + rr->rdlength = 0; + rr->rdata = NULL; + } else { + if (handle->_msg_ptr + NS_INT32SZ + NS_INT16SZ > handle->_eom) + RETERR(EMSGSIZE); + NS_GET32(rr->ttl, handle->_msg_ptr); + NS_GET16(rr->rdlength, handle->_msg_ptr); + if (handle->_msg_ptr + rr->rdlength > handle->_eom) + RETERR(EMSGSIZE); + rr->rdata = handle->_msg_ptr; + handle->_msg_ptr += rr->rdlength; + } + if (++handle->_rrnum > handle->_counts[(int)section]) + setsection(handle, (ns_sect)((int)section + 1)); + + /* All done. */ + return (0); +} + +/* Private. */ + +static void +setsection(ns_msg *msg, ns_sect sect) { + msg->_sect = sect; + if (sect == ns_s_max) { + msg->_rrnum = -1; + msg->_msg_ptr = NULL; + } else { + msg->_rrnum = 0; + msg->_msg_ptr = msg->_sections[(int)sect]; + } +} diff --git a/ns_print.c b/ns_print.c new file mode 100644 index 0000000..fd9c71f --- /dev/null +++ b/ns_print.c @@ -0,0 +1,914 @@ +/* + * Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#ifndef __APPLE__ +#ifndef lint +static const char rcsid[] = "$Id: ns_print.c,v 1.1 2006/03/01 19:01:37 majka Exp $"; +#endif +#endif + +/* Import. */ + +#ifndef __APPLE__ +#include "port_before.h" +#endif + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#ifndef __APPLE__ +#include +#include "port_after.h" +#endif + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/* Forward. */ + +static size_t prune_origin(const char *name, const char *origin); +static int charstr(const u_char *rdata, const u_char *edata, + char **buf, size_t *buflen); +static int addname(const u_char *msg, size_t msglen, + const u_char **p, const char *origin, + char **buf, size_t *buflen); +static void addlen(size_t len, char **buf, size_t *buflen); +static int addstr(const char *src, size_t len, + char **buf, size_t *buflen); +static int addtab(size_t len, size_t target, int spaced, + char **buf, size_t *buflen); + +/* Proto. */ + +#ifndef dst_s_dns_key_id +#define dst_s_dns_key_id res_9_dst_s_dns_key_id +#endif +u_int16_t dst_s_dns_key_id(const u_char *, const int); + +/* Macros. */ + +#define T(x) \ + do { \ + if ((x) < 0) \ + return (-1); \ + } while (0) + +/* Public. */ + +/* + * int + * ns_sprintrr(handle, rr, name_ctx, origin, buf, buflen) + * Convert an RR to presentation format. + * return: + * Number of characters written to buf, or -1 (check errno). + */ +int +ns_sprintrr(const ns_msg *handle, const ns_rr *rr, + const char *name_ctx, const char *origin, + char *buf, size_t buflen) +{ + int n; + + n = ns_sprintrrf(ns_msg_base(*handle), ns_msg_size(*handle), + ns_rr_name(*rr), ns_rr_class(*rr), ns_rr_type(*rr), + ns_rr_ttl(*rr), ns_rr_rdata(*rr), ns_rr_rdlen(*rr), + name_ctx, origin, buf, buflen); + return (n); +} + +/* + * int + * ns_sprintrrf(msg, msglen, name, class, type, ttl, rdata, rdlen, + * name_ctx, origin, buf, buflen) + * Convert the fields of an RR into presentation format. + * return: + * Number of characters written to buf, or -1 (check errno). + */ +int +ns_sprintrrf(const u_char *msg, size_t msglen, + const char *name, ns_class class, ns_type type, + u_long ttl, const u_char *rdata, size_t rdlen, + const char *name_ctx, const char *origin, + char *buf, size_t buflen) +{ + const char *obuf = buf; + const u_char *edata = rdata + rdlen; + int spaced = 0; + + const char *comment; + char tmp[100]; + int len, x; + + /* + * Owner. + */ + if (name_ctx != NULL && ns_samename(name_ctx, name) == 1) { + T(addstr("\t\t\t", 3, &buf, &buflen)); + } else { + len = prune_origin(name, origin); + if (*name == '\0') { + goto root; + } else if (len == 0) { + T(addstr("@\t\t\t", 4, &buf, &buflen)); + } else { + T(addstr(name, len, &buf, &buflen)); + /* Origin not used or not root, and no trailing dot? */ + if (((origin == NULL || origin[0] == '\0') || + (origin[0] != '.' && origin[1] != '\0' && + name[len] == '\0')) && name[len - 1] != '.') { + root: + T(addstr(".", 1, &buf, &buflen)); + len++; + } + T(spaced = addtab(len, 24, spaced, &buf, &buflen)); + } + } + + /* + * TTL, Class, Type. + */ + T(x = ns_format_ttl(ttl, buf, buflen)); + addlen(x, &buf, &buflen); + len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type))); + T(addstr(tmp, len, &buf, &buflen)); + if (rdlen == 0) + return (buf - obuf); + T(spaced = addtab(x + len, 16, spaced, &buf, &buflen)); + + /* + * RData. + */ + switch (type) { + case ns_t_a: + if (rdlen != NS_INADDRSZ) + goto formerr; + (void) inet_ntop(AF_INET, rdata, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + break; + + case ns_t_cname: + case ns_t_mb: + case ns_t_mg: + case ns_t_mr: + case ns_t_ns: + case ns_t_ptr: + case ns_t_dname: + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + break; + + case ns_t_hinfo: + case ns_t_isdn: + /* First word. */ + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + T(addstr(" ", 1, &buf, &buflen)); + + + /* Second word, optional in ISDN records. */ + if (type == ns_t_isdn && rdata == edata) + break; + + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + break; + + case ns_t_soa: { + u_long t; + + /* Server name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + + /* Administrator name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" (\n", 3, &buf, &buflen)); + spaced = 0; + + if ((edata - rdata) != 5*NS_INT32SZ) + goto formerr; + + /* Serial number. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + len = SPRINTF((tmp, "%lu", t)); + T(addstr(tmp, len, &buf, &buflen)); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; serial\n", 9, &buf, &buflen)); + spaced = 0; + + /* Refresh interval. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + T(len = ns_format_ttl(t, buf, buflen)); + addlen(len, &buf, &buflen); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; refresh\n", 10, &buf, &buflen)); + spaced = 0; + + /* Retry interval. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + T(len = ns_format_ttl(t, buf, buflen)); + addlen(len, &buf, &buflen); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; retry\n", 8, &buf, &buflen)); + spaced = 0; + + /* Expiry. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + T(len = ns_format_ttl(t, buf, buflen)); + addlen(len, &buf, &buflen); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; expiry\n", 9, &buf, &buflen)); + spaced = 0; + + /* Minimum TTL. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + T(addstr("\t\t\t\t\t", 5, &buf, &buflen)); + T(len = ns_format_ttl(t, buf, buflen)); + addlen(len, &buf, &buflen); + T(addstr(" )", 2, &buf, &buflen)); + T(spaced = addtab(len, 16, spaced, &buf, &buflen)); + T(addstr("; minimum\n", 10, &buf, &buflen)); + + break; + } + + case ns_t_mx: + case ns_t_afsdb: + case ns_t_rt: { + u_int t; + + if (rdlen < NS_INT16SZ) + goto formerr; + + /* Priority. */ + t = ns_get16(rdata); + rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u ", t)); + T(addstr(tmp, len, &buf, &buflen)); + + /* Target. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + break; + } + + case ns_t_px: { + u_int t; + + if (rdlen < NS_INT16SZ) + goto formerr; + + /* Priority. */ + t = ns_get16(rdata); + rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u ", t)); + T(addstr(tmp, len, &buf, &buflen)); + + /* Name1. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + + /* Name2. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + break; + } + + case ns_t_x25: + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + break; + + case ns_t_txt: + while (rdata < edata) { + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + if (rdata < edata) + T(addstr(" ", 1, &buf, &buflen)); + } + break; + + case ns_t_nsap: { + char t[2+255*3]; + + (void) inet_nsap_ntoa(rdlen, rdata, t); + T(addstr(t, strlen(t), &buf, &buflen)); + break; + } + + case ns_t_aaaa: + if (rdlen != NS_IN6ADDRSZ) + goto formerr; + (void) inet_ntop(AF_INET6, rdata, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + break; + + case ns_t_loc: { + char t[255]; + + /* XXX protocol format checking? */ + (void) loc_ntoa(rdata, t); + T(addstr(t, strlen(t), &buf, &buflen)); + break; + } + + case ns_t_naptr: { + u_int order, preference; + char t[50]; + + if (rdlen < 2*NS_INT16SZ) + goto formerr; + + /* Order, Precedence. */ + order = ns_get16(rdata); rdata += NS_INT16SZ; + preference = ns_get16(rdata); rdata += NS_INT16SZ; + len = SPRINTF((t, "%u %u ", order, preference)); + T(addstr(t, len, &buf, &buflen)); + + /* Flags. */ + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + T(addstr(" ", 1, &buf, &buflen)); + + /* Service. */ + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len == 0) + goto formerr; + rdata += len; + T(addstr(" ", 1, &buf, &buflen)); + + /* Regexp. */ + T(len = charstr(rdata, edata, &buf, &buflen)); + if (len < 0) + return (-1); + if (len == 0) + goto formerr; + rdata += len; + T(addstr(" ", 1, &buf, &buflen)); + + /* Server. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + break; + } + + case ns_t_srv: { + u_int priority, weight, port; + char t[50]; + + if (rdlen < NS_INT16SZ*3) + goto formerr; + + /* Priority, Weight, Port. */ + priority = ns_get16(rdata); rdata += NS_INT16SZ; + weight = ns_get16(rdata); rdata += NS_INT16SZ; + port = ns_get16(rdata); rdata += NS_INT16SZ; + len = SPRINTF((t, "%u %u %u ", priority, weight, port)); + T(addstr(t, len, &buf, &buflen)); + + /* Server. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + break; + } + + case ns_t_minfo: + case ns_t_rp: + /* Name1. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + + /* Name2. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + break; + + case ns_t_wks: { + int n, lcnt; + + if (rdlen < NS_INT32SZ + 1) + goto formerr; + + /* Address. */ + (void) inet_ntop(AF_INET, rdata, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + rdata += NS_INADDRSZ; + + /* Protocol. */ + len = SPRINTF((tmp, " %u ( ", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + rdata += NS_INT8SZ; + + /* Bit map. */ + n = 0; + lcnt = 0; + while (rdata < edata) { + u_int c = *rdata++; + do { + if (c & 0200) { + if (lcnt == 0) { + T(addstr("\n\t\t\t\t", 5, + &buf, &buflen)); + lcnt = 10; + spaced = 0; + } + len = SPRINTF((tmp, "%d ", n)); + T(addstr(tmp, len, &buf, &buflen)); + lcnt--; + } + c <<= 1; + } while (++n & 07); + } + T(addstr(")", 1, &buf, &buflen)); + + break; + } + + case ns_t_key: { + char base64_key[NS_MD5RSA_MAX_BASE64]; + u_int keyflags, protocol, algorithm, key_id; + const char *leader; + int n; + + if (rdlen < NS_INT16SZ + NS_INT8SZ + NS_INT8SZ) + goto formerr; + + /* Key flags, Protocol, Algorithm. */ + key_id = dst_s_dns_key_id(rdata, edata-rdata); + keyflags = ns_get16(rdata); rdata += NS_INT16SZ; + protocol = *rdata++; + algorithm = *rdata++; + len = SPRINTF((tmp, "0x%04x %u %u", + keyflags, protocol, algorithm)); + T(addstr(tmp, len, &buf, &buflen)); + + /* Public key data. */ + len = b64_ntop(rdata, edata - rdata, + base64_key, sizeof base64_key); + if (len < 0) + goto formerr; + if (len > 15) { + T(addstr(" (", 2, &buf, &buflen)); + leader = "\n\t\t"; + spaced = 0; + } else + leader = " "; + for (n = 0; n < len; n += 48) { + T(addstr(leader, strlen(leader), &buf, &buflen)); + T(addstr(base64_key + n, MIN(len - n, 48), + &buf, &buflen)); + } + if (len > 15) + T(addstr(" )", 2, &buf, &buflen)); + n = SPRINTF((tmp, " ; key_tag= %u", key_id)); + T(addstr(tmp, n, &buf, &buflen)); + + break; + } + + case ns_t_sig: { + char base64_key[NS_MD5RSA_MAX_BASE64]; + u_int type, algorithm, labels, footprint; + const char *leader; + u_long t; + int n; + + if (rdlen < 22) + goto formerr; + + /* Type covered, Algorithm, Label count, Original TTL. */ + type = ns_get16(rdata); rdata += NS_INT16SZ; + algorithm = *rdata++; + labels = *rdata++; + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s %d %d %lu ", + p_type(type), algorithm, labels, t)); + T(addstr(tmp, len, &buf, &buflen)); + if (labels > (u_int)dn_count_labels(name)) + goto formerr; + + /* Signature expiry. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s ", p_secstodate(t))); + T(addstr(tmp, len, &buf, &buflen)); + + /* Time signed. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s ", p_secstodate(t))); + T(addstr(tmp, len, &buf, &buflen)); + + /* Signature Footprint. */ + footprint = ns_get16(rdata); rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u ", footprint)); + T(addstr(tmp, len, &buf, &buflen)); + + /* Signer's name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + /* Signature. */ + len = b64_ntop(rdata, edata - rdata, + base64_key, sizeof base64_key); + if (len > 15) { + T(addstr(" (", 2, &buf, &buflen)); + leader = "\n\t\t"; + spaced = 0; + } else + leader = " "; + if (len < 0) + goto formerr; + for (n = 0; n < len; n += 48) { + T(addstr(leader, strlen(leader), &buf, &buflen)); + T(addstr(base64_key + n, MIN(len - n, 48), + &buf, &buflen)); + } + if (len > 15) + T(addstr(" )", 2, &buf, &buflen)); + break; + } + + case ns_t_nxt: { + int n, c; + + /* Next domain name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + /* Type bit map. */ + n = edata - rdata; + for (c = 0; c < n*8; c++) + if (NS_NXT_BIT_ISSET(c, rdata)) { + len = SPRINTF((tmp, " %s", p_type(c))); + T(addstr(tmp, len, &buf, &buflen)); + } + break; + } + + case ns_t_cert: { + u_int c_type, key_tag, alg; + int n; + unsigned int siz; + char base64_cert[8192], tmp[40]; + const char *leader; + + c_type = ns_get16(rdata); rdata += NS_INT16SZ; + key_tag = ns_get16(rdata); rdata += NS_INT16SZ; + alg = (u_int) *rdata++; + + len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg)); + T(addstr(tmp, len, &buf, &buflen)); + siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */ + if (siz > sizeof(base64_cert) * 3/4) { + const char *str = "record too long to print"; + T(addstr(str, strlen(str), &buf, &buflen)); + } + else { + len = b64_ntop(rdata, edata-rdata, base64_cert, siz); + + if (len < 0) + goto formerr; + else if (len > 15) { + T(addstr(" (", 2, &buf, &buflen)); + leader = "\n\t\t"; + spaced = 0; + } + else + leader = " "; + + for (n = 0; n < len; n += 48) { + T(addstr(leader, strlen(leader), + &buf, &buflen)); + T(addstr(base64_cert + n, MIN(len - n, 48), + &buf, &buflen)); + } + if (len > 15) + T(addstr(" )", 2, &buf, &buflen)); + } + break; + } + + case ns_t_tkey: { + /* KJD - need to complete this */ + u_long t; + int mode, err, keysize; + + /* Algorithm name. */ + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + + /* Inception. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s ", p_secstodate(t))); + T(addstr(tmp, len, &buf, &buflen)); + + /* Experation. */ + t = ns_get32(rdata); rdata += NS_INT32SZ; + len = SPRINTF((tmp, "%s ", p_secstodate(t))); + T(addstr(tmp, len, &buf, &buflen)); + + /* Mode , Error, Key Size. */ + /* Priority, Weight, Port. */ + mode = ns_get16(rdata); rdata += NS_INT16SZ; + err = ns_get16(rdata); rdata += NS_INT16SZ; + keysize = ns_get16(rdata); rdata += NS_INT16SZ; + len = SPRINTF((tmp, "%u %u %u ", mode, err, keysize)); + T(addstr(tmp, len, &buf, &buflen)); + + /* needs to dump key, print otherdata length & other data */ + break; + } + case ns_t_tsig: { + /* BEW - need to complete this */ + int n; + + T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen)); + T(addstr(" ", 1, &buf, &buflen)); + rdata += 8; /* time */ + n = ns_get16(rdata); rdata += NS_INT16SZ; + rdata += n; /* sig */ + n = ns_get16(rdata); rdata += NS_INT16SZ; /* original id */ + sprintf(buf, "%d", ns_get16(rdata)); + rdata += NS_INT16SZ; + addlen(strlen(buf), &buf, &buflen); + break; + } + + case ns_t_a6: { + struct in6_addr a; + int pbyte, pbit; + + /* prefix length */ + if (rdlen == 0) goto formerr; + len = SPRINTF((tmp, "%d ", *rdata)); + T(addstr(tmp, len, &buf, &buflen)); + pbit = *rdata; + if (pbit > 128) goto formerr; + pbyte = (pbit & ~7) / 8; + rdata++; + + /* address suffix: provided only when prefix len != 128 */ + if (pbit < 128) { + if (rdata + pbyte >= edata) goto formerr; + memset(&a, 0, sizeof(a)); + memcpy(&a.s6_addr[pbyte], rdata, sizeof(a) - pbyte); + (void) inet_ntop(AF_INET6, &a, buf, buflen); + addlen(strlen(buf), &buf, &buflen); + rdata += sizeof(a) - pbyte; + } + + /* prefix name: provided only when prefix len > 0 */ + if (pbit == 0) + break; + if (rdata >= edata) goto formerr; + T(addstr(" ", 1, &buf, &buflen)); + T(addname(msg, msglen, &rdata, origin, &buf, &buflen)); + + break; + } + + case ns_t_opt: { + len = SPRINTF((tmp, "%u bytes", class)); + T(addstr(tmp, len, &buf, &buflen)); + break; + } + + default: + comment = "unknown RR type"; + goto hexify; + } + return (buf - obuf); + formerr: + comment = "RR format error"; + hexify: { + int n, m; + char *p; + + len = SPRINTF((tmp, "\\# %u (\t; %s", edata - rdata, comment)); + T(addstr(tmp, len, &buf, &buflen)); + while (rdata < edata) { + p = tmp; + p += SPRINTF((p, "\n\t")); + spaced = 0; + n = MIN(16, edata - rdata); + for (m = 0; m < n; m++) + p += SPRINTF((p, "%02x ", rdata[m])); + T(addstr(tmp, p - tmp, &buf, &buflen)); + if (n < 16) { + T(addstr(")", 1, &buf, &buflen)); + T(addtab(p - tmp + 1, 48, spaced, &buf, &buflen)); + } + p = tmp; + p += SPRINTF((p, "; ")); + for (m = 0; m < n; m++) + *p++ = (isascii(rdata[m]) && isprint(rdata[m])) + ? rdata[m] + : '.'; + T(addstr(tmp, p - tmp, &buf, &buflen)); + rdata += n; + } + return (buf - obuf); + } +} + +/* Private. */ + +/* + * size_t + * prune_origin(name, origin) + * Find out if the name is at or under the current origin. + * return: + * Number of characters in name before start of origin, + * or length of name if origin does not match. + * notes: + * This function should share code with samedomain(). + */ +static size_t +prune_origin(const char *name, const char *origin) { + const char *oname = name; + + while (*name != '\0') { + if (origin != NULL && ns_samename(name, origin) == 1) + return (name - oname - (name > oname)); + while (*name != '\0') { + if (*name == '\\') { + name++; + /* XXX need to handle \nnn form. */ + if (*name == '\0') + break; + } else if (*name == '.') { + name++; + break; + } + name++; + } + } + return (name - oname); +} + +/* + * int + * charstr(rdata, edata, buf, buflen) + * Format a into the presentation buffer. + * return: + * Number of rdata octets consumed + * 0 for protocol format error + * -1 for output buffer error + * side effects: + * buffer is advanced on success. + */ +static int +charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen) { + const u_char *odata = rdata; + size_t save_buflen = *buflen; + char *save_buf = *buf; + + if (addstr("\"", 1, buf, buflen) < 0) + goto enospc; + if (rdata < edata) { + int n = *rdata; + + if (rdata + 1 + n <= edata) { + rdata++; + while (n-- > 0) { + if (strchr("\n\"\\", *rdata) != NULL) + if (addstr("\\", 1, buf, buflen) < 0) + goto enospc; + if (addstr((const char *)rdata, 1, + buf, buflen) < 0) + goto enospc; + rdata++; + } + } + } + if (addstr("\"", 1, buf, buflen) < 0) + goto enospc; + return (rdata - odata); + enospc: + errno = ENOSPC; + *buf = save_buf; + *buflen = save_buflen; + return (-1); +} + +static int +addname(const u_char *msg, size_t msglen, + const u_char **pp, const char *origin, + char **buf, size_t *buflen) +{ + size_t newlen, save_buflen = *buflen; + char *save_buf = *buf; + int n; + + n = dn_expand(msg, msg + msglen, *pp, *buf, *buflen); + if (n < 0) + goto enospc; /* Guess. */ + newlen = prune_origin(*buf, origin); + if (**buf == '\0') { + goto root; + } else if (newlen == 0) { + /* Use "@" instead of name. */ + if (newlen + 2 > *buflen) + goto enospc; /* No room for "@\0". */ + (*buf)[newlen++] = '@'; + (*buf)[newlen] = '\0'; + } else { + if (((origin == NULL || origin[0] == '\0') || + (origin[0] != '.' && origin[1] != '\0' && + (*buf)[newlen] == '\0')) && (*buf)[newlen - 1] != '.') { + /* No trailing dot. */ + root: + if (newlen + 2 > *buflen) + goto enospc; /* No room for ".\0". */ + (*buf)[newlen++] = '.'; + (*buf)[newlen] = '\0'; + } + } + *pp += n; + addlen(newlen, buf, buflen); + **buf = '\0'; + return (newlen); + enospc: + errno = ENOSPC; + *buf = save_buf; + *buflen = save_buflen; + return (-1); +} + +static void +addlen(size_t len, char **buf, size_t *buflen) { +#ifdef __APPLE__ + if (len > *buflen) return; +#else + INSIST(len <= *buflen); +#endif + *buf += len; + *buflen -= len; +} + +static int +addstr(const char *src, size_t len, char **buf, size_t *buflen) { + if (len >= *buflen) { + errno = ENOSPC; + return (-1); + } + memcpy(*buf, src, len); + addlen(len, buf, buflen); + **buf = '\0'; + return (0); +} + +static int +addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen) { + size_t save_buflen = *buflen; + char *save_buf = *buf; + int t; + + if (spaced || len >= target - 1) { + T(addstr(" ", 2, buf, buflen)); + spaced = 1; + } else { + for (t = (target - len - 1) / 8; t >= 0; t--) + if (addstr("\t", 1, buf, buflen) < 0) { + *buflen = save_buflen; + *buf = save_buf; + return (-1); + } + spaced = 0; + } + return (spaced); +} diff --git a/ns_samedomain.c b/ns_samedomain.c new file mode 100644 index 0000000..1d53bb2 --- /dev/null +++ b/ns_samedomain.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 1995,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#ifndef __APPLE__ +#ifndef lint +static const char rcsid[] = "$Id: ns_samedomain.c,v 1.1 2006/03/01 19:01:37 majka Exp $"; +#endif +#endif + +#ifndef __APPLE__ +#include "port_before.h" +#endif + +#include +#include +#include +#include + +#ifndef __APPLE__ +#include "port_after.h" +#endif + +/* + * int + * ns_samedomain(a, b) + * Check whether a name belongs to a domain. + * Inputs: + * a - the domain whose ancestory is being verified + * b - the potential ancestor we're checking against + * Return: + * boolean - is a at or below b? + * Notes: + * Trailing dots are first removed from name and domain. + * Always compare complete subdomains, not only whether the + * domain name is the trailing string of the given name. + * + * "host.foobar.top" lies in "foobar.top" and in "top" and in "" + * but NOT in "bar.top" + */ + +int +ns_samedomain(const char *a, const char *b) { + size_t la, lb; + int diff, i, escaped; + const char *cp; + + la = strlen(a); + lb = strlen(b); + + /* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */ + if (la != 0 && a[la - 1] == '.') { + escaped = 0; + /* Note this loop doesn't get executed if la==1. */ + for (i = la - 2; i >= 0; i--) + if (a[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else + break; + if (!escaped) + la--; + } + + /* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */ + if (lb != 0 && b[lb - 1] == '.') { + escaped = 0; + /* note this loop doesn't get executed if lb==1 */ + for (i = lb - 2; i >= 0; i--) + if (b[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else + break; + if (!escaped) + lb--; + } + + /* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */ + if (lb == 0) + return (1); + + /* 'b' longer than 'a' means 'a' can't be in 'b'. */ + if (lb > la) + return (0); + + /* 'a' and 'b' being equal at this point indicates sameness. */ + if (lb == la) + return (strncasecmp(a, b, lb) == 0); + + /* Ok, we know la > lb. */ + + diff = la - lb; + + /* + * If 'a' is only 1 character longer than 'b', then it can't be + * a subdomain of 'b' (because of the need for the '.' label + * separator). + */ + if (diff < 2) + return (0); + + /* + * If the character before the last 'lb' characters of 'b' + * isn't '.', then it can't be a match (this lets us avoid + * having "foobar.com" match "bar.com"). + */ + if (a[diff - 1] != '.') + return (0); + + /* + * We're not sure about that '.', however. It could be escaped + * and thus not a really a label separator. + */ + escaped = 0; + for (i = diff - 2; i >= 0; i--) + if (a[i] == '\\') { + if (escaped) + escaped = 0; + else + escaped = 1; + } else + break; + if (escaped) + return (0); + + /* Now compare aligned trailing substring. */ + cp = a + diff; + return (strncasecmp(cp, b, lb) == 0); +} + +/* + * int + * ns_subdomain(a, b) + * is "a" a subdomain of "b"? + */ +int +ns_subdomain(const char *a, const char *b) { + return (ns_samename(a, b) != 1 && ns_samedomain(a, b)); +} + +/* + * int + * ns_makecanon(src, dst, dstsize) + * make a canonical copy of domain name "src" + * notes: + * foo -> foo. + * foo. -> foo. + * foo.. -> foo. + * foo\. -> foo\.. + * foo\\. -> foo\\. + */ + +int +ns_makecanon(const char *src, char *dst, size_t dstsize) { + size_t n = strlen(src); + + if (n + sizeof "." > dstsize) { + errno = EMSGSIZE; + return (-1); + } + strcpy(dst, src); + while (n > 0 && dst[n - 1] == '.') /* Ends in "." */ + if (n > 1 && dst[n - 2] == '\\' && /* Ends in "\." */ + (n < 2 || dst[n - 3] != '\\')) /* But not "\\." */ + break; + else + dst[--n] = '\0'; + dst[n++] = '.'; + dst[n] = '\0'; + return (0); +} + +/* + * int + * ns_samename(a, b) + * determine whether domain name "a" is the same as domain name "b" + * return: + * -1 on error + * 0 if names differ + * 1 if names are the same + */ + +int +ns_samename(const char *a, const char *b) { + char ta[NS_MAXDNAME], tb[NS_MAXDNAME]; + + if (ns_makecanon(a, ta, sizeof ta) < 0 || + ns_makecanon(b, tb, sizeof tb) < 0) + return (-1); + if (strcasecmp(ta, tb) == 0) + return (1); + else + return (0); +} diff --git a/ns_sign.c b/ns_sign.c new file mode 100644 index 0000000..f19077a --- /dev/null +++ b/ns_sign.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 1999 by Internet Software Consortium, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#ifndef __APPLE__ +#ifndef lint +static const char rcsid[] = "$Id: ns_sign.c,v 1.1 2006/03/01 19:01:37 majka Exp $"; +#endif +#endif + +/* Import. */ + +#ifndef __APPLE__ +#include "port_before.h" +#include "fd_setsize.h" +#endif + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __APPLE__ +#include +#include "port_after.h" +#else +#include "dst_internal.h" +#include "res_private.h" +#endif + +#define BOUNDS_CHECK(ptr, count) \ + do { \ + if ((ptr) + (count) > eob) { \ + errno = EMSGSIZE; \ + return(NS_TSIG_ERROR_NO_SPACE); \ + } \ + } while (0) + +/* ns_sign + * Parameters: + * msg message to be sent + * msglen input - length of message + * output - length of signed message + * msgsize length of buffer containing message + * error value to put in the error field + * key tsig key used for signing + * querysig (response), the signature in the query + * querysiglen (response), the length of the signature in the query + * sig a buffer to hold the generated signature + * siglen input - length of signature buffer + * output - length of signature + * + * Errors: + * - bad input data (-1) + * - bad key / sign failed (-BADKEY) + * - not enough space (NS_TSIG_ERROR_NO_SPACE) + */ +int +ns_sign(u_char *msg, int *msglen, int msgsize, int error, void *k, + const u_char *querysig, int querysiglen, u_char *sig, int *siglen, + time_t in_timesigned) +{ + return(ns_sign2(msg, msglen, msgsize, error, k, + querysig, querysiglen, sig, siglen, + in_timesigned, NULL, NULL)); +} + +int +ns_sign2(u_char *msg, int *msglen, int msgsize, int error, void *k, + const u_char *querysig, int querysiglen, u_char *sig, int *siglen, + time_t in_timesigned, u_char **dnptrs, u_char **lastdnptr) +{ + HEADER *hp = (HEADER *)msg; + DST_KEY *key = (DST_KEY *)k; + u_char *cp = msg + *msglen, *eob = msg + msgsize; + u_char *lenp; + u_char *name, *alg; + int n; + time_t timesigned; + + dst_init(); + if (msg == NULL || msglen == NULL || sig == NULL || siglen == NULL) + return (-1); + + /* Name. */ + if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) + n = dn_comp(key->dk_key_name, cp, eob - cp, dnptrs, lastdnptr); + else + n = dn_comp("", cp, eob - cp, NULL, NULL); + if (n < 0) + return (NS_TSIG_ERROR_NO_SPACE); + name = cp; + cp += n; + + /* Type, class, ttl, length (not filled in yet). */ + BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT16SZ + NS_INT32SZ + NS_INT16SZ); + NS_PUT16(ns_t_tsig, cp); + NS_PUT16(ns_c_any, cp); + NS_PUT32(0, cp); /* TTL */ + lenp = cp; + cp += 2; + + /* Alg. */ + if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { + if (key->dk_alg != KEY_HMAC_MD5) + return (-ns_r_badkey); + n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL); + } + else + n = dn_comp("", cp, eob - cp, NULL, NULL); + if (n < 0) + return (NS_TSIG_ERROR_NO_SPACE); + alg = cp; + cp += n; + + /* Time. */ + BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT32SZ + NS_INT16SZ); + NS_PUT16(0, cp); + timesigned = time(NULL); + if (error != ns_r_badtime) + NS_PUT32(timesigned, cp); + else + NS_PUT32(in_timesigned, cp); + NS_PUT16(NS_TSIG_FUDGE, cp); + + /* Compute the signature. */ + if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { + void *ctx; + u_char buf[NS_MAXDNAME], *cp2; + int n; + + dst_sign_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0); + + /* Digest the query signature, if this is a response. */ + if (querysiglen > 0 && querysig != NULL) { + u_int16_t len_n = htons(querysiglen); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, + (u_char *)&len_n, NS_INT16SZ, NULL, 0); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, + querysig, querysiglen, NULL, 0); + } + + /* Digest the message. */ + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, msg, *msglen, + NULL, 0); + + /* Digest the key name. */ + n = ns_name_ntol(name, buf, sizeof(buf)); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); + + /* Digest the class and TTL. */ + cp2 = buf; + NS_PUT16(ns_c_any, cp2); + NS_PUT32(0, cp2); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf, + NULL, 0); + + /* Digest the algorithm. */ + n = ns_name_ntol(alg, buf, sizeof(buf)); + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); + + /* Digest the time signed, fudge, error, and other data */ + cp2 = buf; + NS_PUT16(0, cp2); /* Top 16 bits of time */ + if (error != ns_r_badtime) + NS_PUT32(timesigned, cp2); + else + NS_PUT32(in_timesigned, cp2); + NS_PUT16(NS_TSIG_FUDGE, cp2); + NS_PUT16(error, cp2); /* Error */ + if (error != ns_r_badtime) + NS_PUT16(0, cp2); /* Other data length */ + else { + NS_PUT16(NS_INT16SZ+NS_INT32SZ, cp2); /* Other data length */ + NS_PUT16(0, cp2); /* Top 16 bits of time */ + NS_PUT32(timesigned, cp2); + } + dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf, + NULL, 0); + + n = dst_sign_data(SIG_MODE_FINAL, key, &ctx, NULL, 0, + sig, *siglen); + if (n < 0) + return (-ns_r_badkey); + *siglen = n; + } else + *siglen = 0; + + /* Add the signature. */ + BOUNDS_CHECK(cp, NS_INT16SZ + (*siglen)); + NS_PUT16(*siglen, cp); + memcpy(cp, sig, *siglen); + cp += (*siglen); + + /* The original message ID & error. */ + BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT16SZ); + NS_PUT16(ntohs(hp->id), cp); /* already in network order */ + NS_PUT16(error, cp); + + /* Other data. */ + BOUNDS_CHECK(cp, NS_INT16SZ); + if (error != ns_r_badtime) + NS_PUT16(0, cp); /* Other data length */ + else { + NS_PUT16(NS_INT16SZ+NS_INT32SZ, cp); /* Other data length */ + BOUNDS_CHECK(cp, NS_INT32SZ+NS_INT16SZ); + NS_PUT16(0, cp); /* Top 16 bits of time */ + NS_PUT32(timesigned, cp); + } + + /* Go back and fill in the length. */ + NS_PUT16(cp - lenp - NS_INT16SZ, lenp); + + hp->arcount = htons(ntohs(hp->arcount) + 1); + *msglen = (cp - msg); + return (0); +} + +int +ns_sign_tcp_init(void *k, const u_char *querysig, int querysiglen, + ns_tcp_tsig_state *state) +{ + dst_init(); + if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0) + return (-1); + state->counter = -1; + state->key = k; + if (state->key->dk_alg != KEY_HMAC_MD5) + return (-ns_r_badkey); + if (querysiglen > (int)sizeof(state->sig)) + return (-1); + memcpy(state->sig, querysig, querysiglen); + state->siglen = querysiglen; + return (0); +} + +int +ns_sign_tcp(u_char *msg, int *msglen, int msgsize, int error, + ns_tcp_tsig_state *state, int done) +{ + return (ns_sign_tcp2(msg, msglen, msgsize, error, state, + done, NULL, NULL)); +} + +int +ns_sign_tcp2(u_char *msg, int *msglen, int msgsize, int error, + ns_tcp_tsig_state *state, int done, + u_char **dnptrs, u_char **lastdnptr) +{ + u_char *cp, *eob, *lenp; + u_char buf[NS_MAXDNAME], *cp2; + HEADER *hp = (HEADER *)msg; + time_t timesigned; + int n; + + if (msg == NULL || msglen == NULL || state == NULL) + return (-1); + + state->counter++; + if (state->counter == 0) + return (ns_sign2(msg, msglen, msgsize, error, state->key, + state->sig, state->siglen, + state->sig, &state->siglen, 0, + dnptrs, lastdnptr)); + + if (state->siglen > 0) { + u_int16_t siglen_n = htons(state->siglen); + dst_sign_data(SIG_MODE_INIT, state->key, &state->ctx, + NULL, 0, NULL, 0); + dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, + (u_char *)&siglen_n, NS_INT16SZ, NULL, 0); + dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, + state->sig, state->siglen, NULL, 0); + state->siglen = 0; + } + + dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, msg, *msglen, + NULL, 0); + + if (done == 0 && (state->counter % 100 != 0)) + return (0); + + cp = msg + *msglen; + eob = msg + msgsize; + + /* Name. */ + n = dn_comp(state->key->dk_key_name, cp, eob - cp, dnptrs, lastdnptr); + if (n < 0) + return (NS_TSIG_ERROR_NO_SPACE); + cp += n; + + /* Type, class, ttl, length (not filled in yet). */ + BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT16SZ + NS_INT32SZ + NS_INT16SZ); + NS_PUT16(ns_t_tsig, cp); + NS_PUT16(ns_c_any, cp); + NS_PUT32(0, cp); /* TTL */ + lenp = cp; + cp += 2; + + /* Alg. */ + n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL); + if (n < 0) + return (NS_TSIG_ERROR_NO_SPACE); + cp += n; + + /* Time. */ + BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT32SZ + NS_INT16SZ); + NS_PUT16(0, cp); + timesigned = time(NULL); + NS_PUT32(timesigned, cp); + NS_PUT16(NS_TSIG_FUDGE, cp); + + /* + * Compute the signature. + */ + + /* Digest the time signed and fudge. */ + cp2 = buf; + NS_PUT16(0, cp2); /* Top 16 bits of time */ + NS_PUT32(timesigned, cp2); + NS_PUT16(NS_TSIG_FUDGE, cp2); + + dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, + buf, cp2 - buf, NULL, 0); + + n = dst_sign_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0, + state->sig, sizeof(state->sig)); + if (n < 0) + return (-ns_r_badkey); + state->siglen = n; + + /* Add the signature. */ + BOUNDS_CHECK(cp, NS_INT16SZ + state->siglen); + NS_PUT16(state->siglen, cp); + memcpy(cp, state->sig, state->siglen); + cp += state->siglen; + + /* The original message ID & error. */ + BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT16SZ); + NS_PUT16(ntohs(hp->id), cp); /* already in network order */ + NS_PUT16(error, cp); + + /* Other data. */ + BOUNDS_CHECK(cp, NS_INT16SZ); + NS_PUT16(0, cp); + + /* Go back and fill in the length. */ + NS_PUT16(cp - lenp - NS_INT16SZ, lenp); + + hp->arcount = htons(ntohs(hp->arcount) + 1); + *msglen = (cp - msg); + return (0); +} diff --git a/ns_ttl.c b/ns_ttl.c new file mode 100644 index 0000000..5a88f24 --- /dev/null +++ b/ns_ttl.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#ifndef __APPLE__ +#ifndef lint +static const char rcsid[] = "$Id: ns_ttl.c,v 1.1 2006/03/01 19:01:37 majka Exp $"; +#endif +#endif + +/* Import. */ + +#ifndef __APPLE__ +#include "port_before.h" +#endif + +#include + +#include +#include +#include +#include + +#ifndef __APPLE__ +#include "port_after.h" +#endif + +#ifdef SPRINTF_CHAR +# define SPRINTF(x) strlen(sprintf/**/x) +#else +# define SPRINTF(x) ((size_t)sprintf x) +#endif + +/* Forward. */ + +static int fmt1(int t, char s, char **buf, size_t *buflen); + +/* Macros. */ + +#define T(x) if ((x) < 0) return (-1); else (void)NULL + +/* Public. */ + +int +ns_format_ttl(u_long src, char *dst, size_t dstlen) { + char *odst = dst; + int secs, mins, hours, days, weeks, x; + char *p; + + secs = src % 60; src /= 60; + mins = src % 60; src /= 60; + hours = src % 24; src /= 24; + days = src % 7; src /= 7; + weeks = src; src = 0; + + x = 0; + if (weeks) { + T(fmt1(weeks, 'W', &dst, &dstlen)); + x++; + } + if (days) { + T(fmt1(days, 'D', &dst, &dstlen)); + x++; + } + if (hours) { + T(fmt1(hours, 'H', &dst, &dstlen)); + x++; + } + if (mins) { + T(fmt1(mins, 'M', &dst, &dstlen)); + x++; + } + if (secs || !(weeks || days || hours || mins)) { + T(fmt1(secs, 'S', &dst, &dstlen)); + x++; + } + + if (x > 1) { + int ch; + + for (p = odst; (ch = *p) != '\0'; p++) + if (isascii(ch) && isupper(ch)) + *p = tolower(ch); + } + + return (dst - odst); +} + +int +ns_parse_ttl(const char *src, u_long *dst) { + u_long ttl, tmp; + int ch, digits, dirty; + + ttl = 0; + tmp = 0; + digits = 0; + dirty = 0; + while ((ch = *src++) != '\0') { + if (!isascii(ch) || !isprint(ch)) + goto einval; + if (isdigit(ch)) { + tmp *= 10; + tmp += (ch - '0'); + digits++; + continue; + } + if (digits == 0) + goto einval; + if (islower(ch)) + ch = toupper(ch); + switch (ch) { + case 'W': tmp *= 7; + case 'D': tmp *= 24; + case 'H': tmp *= 60; + case 'M': tmp *= 60; + case 'S': break; + default: goto einval; + } + ttl += tmp; + tmp = 0; + digits = 0; + dirty = 1; + } + if (digits > 0) { + if (dirty) + goto einval; + else + ttl += tmp; + } + *dst = ttl; + return (0); + + einval: + errno = EINVAL; + return (-1); +} + +/* Private. */ + +static int +fmt1(int t, char s, char **buf, size_t *buflen) { + char tmp[50]; + size_t len; + + len = SPRINTF((tmp, "%d%c", t, s)); + if (len + 1 > *buflen) + return (-1); + strcpy(*buf, tmp); + *buf += len; + *buflen -= len; + return (0); +} diff --git a/ns_verify.c b/ns_verify.c new file mode 100644 index 0000000..58b876b --- /dev/null +++ b/ns_verify.c @@ -0,0 +1,488 @@ +/* + * Copyright (c) 1999 by Internet Software Consortium, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#ifndef __APPLE__ +#ifndef lint +static const char rcsid[] = "$Id: ns_verify.c,v 1.1 2006/03/01 19:01:37 majka Exp $"; +#endif +#endif + +/* Import. */ + +#ifndef __APPLE__ +#include "port_before.h" +#include "fd_setsize.h" +#endif + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __APPLE__ +#include +#include "port_after.h" +#else +#include "dst_internal.h" +#include "res_private.h" +#endif + +/* Private. */ + +#define BOUNDS_CHECK(ptr, count) \ + do { \ + if ((ptr) + (count) > eom) { \ + return (NS_TSIG_ERROR_FORMERR); \ + } \ + } while (0) + +/* Public. */ + +u_char * +ns_find_tsig(u_char *msg, u_char *eom) { + HEADER *hp = (HEADER *)msg; + int n, type; + u_char *cp = msg, *start; + + if (msg == NULL || eom == NULL || msg > eom) + return (NULL); + + if (cp + NS_HFIXEDSZ >= eom) + return (NULL); + + if (hp->arcount == 0) + return (NULL); + + cp += NS_HFIXEDSZ; + + n = ns_skiprr(cp, eom, ns_s_qd, ntohs(hp->qdcount)); + if (n < 0) + return (NULL); + cp += n; + + n = ns_skiprr(cp, eom, ns_s_an, ntohs(hp->ancount)); + if (n < 0) + return (NULL); + cp += n; + + n = ns_skiprr(cp, eom, ns_s_ns, ntohs(hp->nscount)); + if (n < 0) + return (NULL); + cp += n; + + n = ns_skiprr(cp, eom, ns_s_ar, ntohs(hp->arcount) - 1); + if (n < 0) + return (NULL); + cp += n; + + start = cp; + n = dn_skipname(cp, eom); + if (n < 0) + return (NULL); + cp += n; + if (cp + NS_INT16SZ >= eom) + return (NULL); + + NS_GET16(type, cp); + if (type != ns_t_tsig) + return (NULL); + return (start); +} + +/* ns_verify + * Parameters: + * statp res stuff + * msg received message + * msglen length of message + * key tsig key used for verifying. + * querysig (response), the signature in the query + * querysiglen (response), the length of the signature in the query + * sig (query), a buffer to hold the signature + * siglen (query), input - length of signature buffer + * output - length of signature + * + * Errors: + * - bad input (-1) + * - invalid dns message (NS_TSIG_ERROR_FORMERR) + * - TSIG is not present (NS_TSIG_ERROR_NO_TSIG) + * - key doesn't match (-ns_r_badkey) + * - TSIG verification fails with BADKEY (-ns_r_badkey) + * - TSIG verification fails with BADSIG (-ns_r_badsig) + * - TSIG verification fails with BADTIME (-ns_r_badtime) + * - TSIG verification succeeds, error set to BAKEY (ns_r_badkey) + * - TSIG verification succeeds, error set to BADSIG (ns_r_badsig) + * - TSIG verification succeeds, error set to BADTIME (ns_r_badtime) + */ +int +ns_verify(u_char *msg, int *msglen, void *k, + const u_char *querysig, int querysiglen, u_char *sig, int *siglen, + time_t *timesigned, int nostrip) +{ + HEADER *hp = (HEADER *)msg; + DST_KEY *key = (DST_KEY *)k; + u_char *cp = msg, *eom; + char name[NS_MAXDNAME], alg[NS_MAXDNAME]; + u_char *recstart, *rdatastart; + u_char *sigstart, *otherstart; + int n; + int error; + u_int16_t type, length; + u_int16_t fudge, sigfieldlen, id, otherfieldlen; + + dst_init(); + if (msg == NULL || msglen == NULL || *msglen < 0) + return (-1); + + eom = msg + *msglen; + + recstart = ns_find_tsig(msg, eom); + if (recstart == NULL) + return (NS_TSIG_ERROR_NO_TSIG); + + cp = recstart; + + /* Read the key name. */ + n = dn_expand(msg, eom, cp, name, NS_MAXDNAME); + if (n < 0) + return (NS_TSIG_ERROR_FORMERR); + cp += n; + + /* Read the type. */ + BOUNDS_CHECK(cp, 2*NS_INT16SZ + NS_INT32SZ + NS_INT16SZ); + NS_GET16(type, cp); + if (type != ns_t_tsig) + return (NS_TSIG_ERROR_NO_TSIG); + + /* Skip the class and TTL, save the length. */ + cp += NS_INT16SZ + NS_INT32SZ; + NS_GET16(length, cp); + if (eom - cp != length) + return (NS_TSIG_ERROR_FORMERR); + + /* Read the algorithm name. */ + rdatastart = cp; + n = dn_expand(msg, eom, cp, alg, NS_MAXDNAME); + if (n < 0) + return (NS_TSIG_ERROR_FORMERR); + if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1) + return (-ns_r_badkey); + cp += n; + + /* Read the time signed and fudge. */ + BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT32SZ + NS_INT16SZ); + cp += NS_INT16SZ; + NS_GET32((*timesigned), cp); + NS_GET16(fudge, cp); + + /* Read the signature. */ + BOUNDS_CHECK(cp, NS_INT16SZ); + NS_GET16(sigfieldlen, cp); + BOUNDS_CHECK(cp, sigfieldlen); + sigstart = cp; + cp += sigfieldlen; + + /* Read the original id and error. */ + BOUNDS_CHECK(cp, 2*NS_INT16SZ); + NS_GET16(id, cp); + NS_GET16(error, cp); + + /* Parse the other data. */ + BOUNDS_CHECK(cp, NS_INT16SZ); + NS_GET16(otherfieldlen, cp); + BOUNDS_CHECK(cp, otherfieldlen); + otherstart = cp; + cp += otherfieldlen; + + if (cp != eom) + return (NS_TSIG_ERROR_FORMERR); + + /* Verify that the key used is OK. */ + if (key != NULL) { + if (key->dk_alg != KEY_HMAC_MD5) + return (-ns_r_badkey); + if (error != ns_r_badsig && error != ns_r_badkey) { + if (ns_samename(key->dk_key_name, name) != 1) + return (-ns_r_badkey); + } + } + + hp->arcount = htons(ntohs(hp->arcount) - 1); + + /* + * Do the verification. + */ + + if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) { + void *ctx; + u_char buf[NS_MAXDNAME]; + u_char buf2[NS_MAXDNAME]; + + /* Digest the query signature, if this is a response. */ + dst_verify_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0); + if (querysiglen > 0 && querysig != NULL) { + u_int16_t len_n = htons(querysiglen); + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + (u_char *)&len_n, NS_INT16SZ, NULL, 0); + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + querysig, querysiglen, NULL, 0); + } + + /* Digest the message. */ + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, msg, recstart - msg, + NULL, 0); + + /* Digest the key name. */ + n = ns_name_pton(name, buf2, sizeof(buf2)); + if (n < 0) + return (-1); + n = ns_name_ntol(buf2, buf, sizeof(buf)); + if (n < 0) + return (-1); + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); + + /* Digest the class and TTL. */ + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + recstart + dn_skipname(recstart, eom) + NS_INT16SZ, + NS_INT16SZ + NS_INT32SZ, NULL, 0); + + /* Digest the algorithm. */ + n = ns_name_pton(alg, buf2, sizeof(buf2)); + if (n < 0) + return (-1); + n = ns_name_ntol(buf2, buf, sizeof(buf)); + if (n < 0) + return (-1); + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0); + + /* Digest the time signed and fudge. */ + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + rdatastart + dn_skipname(rdatastart, eom), + NS_INT16SZ + NS_INT32SZ + NS_INT16SZ, NULL, 0); + + /* Digest the error and other data. */ + dst_verify_data(SIG_MODE_UPDATE, key, &ctx, + otherstart - NS_INT16SZ - NS_INT16SZ, + otherfieldlen + NS_INT16SZ + NS_INT16SZ, NULL, 0); + + n = dst_verify_data(SIG_MODE_FINAL, key, &ctx, NULL, 0, + sigstart, sigfieldlen); + + if (n < 0) + return (-ns_r_badsig); + + if (sig != NULL && siglen != NULL) { + if (*siglen < sigfieldlen) + return (NS_TSIG_ERROR_NO_SPACE); + memcpy(sig, sigstart, sigfieldlen); + *siglen = sigfieldlen; + } + } else { + if (sigfieldlen > 0) + return (NS_TSIG_ERROR_FORMERR); + if (sig != NULL && siglen != NULL) + *siglen = 0; + } + + /* Reset the counter, since we still need to check for badtime. */ + hp->arcount = htons(ntohs(hp->arcount) + 1); + + /* Verify the time. */ + if (abs((*timesigned) - time(NULL)) > fudge) + return (-ns_r_badtime); + + if (nostrip == 0) { + *msglen = recstart - msg; + hp->arcount = htons(ntohs(hp->arcount) - 1); + } + + if (error != ns_r_noerror) + return (error); + + return (0); +} + +int +ns_verify_tcp_init(void *k, const u_char *querysig, int querysiglen, + ns_tcp_tsig_state *state) +{ + dst_init(); + if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0) + return (-1); + state->counter = -1; + state->key = k; + if (state->key->dk_alg != KEY_HMAC_MD5) + return (-ns_r_badkey); + if (querysiglen > (int)sizeof(state->sig)) + return (-1); + memcpy(state->sig, querysig, querysiglen); + state->siglen = querysiglen; + return (0); +} + +int +ns_verify_tcp(u_char *msg, int *msglen, ns_tcp_tsig_state *state, + int required) +{ + HEADER *hp = (HEADER *)msg; + u_char *recstart, *rdatastart, *sigstart; + unsigned int sigfieldlen, otherfieldlen; + u_char *cp, *eom = msg + *msglen, *cp2; + char name[NS_MAXDNAME], alg[NS_MAXDNAME]; + u_char buf[NS_MAXDNAME]; + int n, type, length, fudge, id, error; + time_t timesigned; + + if (msg == NULL || msglen == NULL || state == NULL) + return (-1); + + state->counter++; + if (state->counter == 0) + return (ns_verify(msg, msglen, state->key, + state->sig, state->siglen, + state->sig, &state->siglen, ×igned, 0)); + + if (state->siglen > 0) { + u_int16_t siglen_n = htons(state->siglen); + + dst_verify_data(SIG_MODE_INIT, state->key, &state->ctx, + NULL, 0, NULL, 0); + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + (u_char *)&siglen_n, NS_INT16SZ, NULL, 0); + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + state->sig, state->siglen, NULL, 0); + state->siglen = 0; + } + + cp = recstart = ns_find_tsig(msg, eom); + + if (recstart == NULL) { + if (required) + return (NS_TSIG_ERROR_NO_TSIG); + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + msg, *msglen, NULL, 0); + return (0); + } + + hp->arcount = htons(ntohs(hp->arcount) - 1); + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + msg, recstart - msg, NULL, 0); + + /* Read the key name. */ + n = dn_expand(msg, eom, cp, name, NS_MAXDNAME); + if (n < 0) + return (NS_TSIG_ERROR_FORMERR); + cp += n; + + /* Read the type. */ + BOUNDS_CHECK(cp, 2*NS_INT16SZ + NS_INT32SZ + NS_INT16SZ); + NS_GET16(type, cp); + if (type != ns_t_tsig) + return (NS_TSIG_ERROR_NO_TSIG); + + /* Skip the class and TTL, save the length. */ + cp += NS_INT16SZ + NS_INT32SZ; + NS_GET16(length, cp); + if (eom - cp != length) + return (NS_TSIG_ERROR_FORMERR); + + /* Read the algorithm name. */ + rdatastart = cp; + n = dn_expand(msg, eom, cp, alg, NS_MAXDNAME); + if (n < 0) + return (NS_TSIG_ERROR_FORMERR); + if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1) + return (-ns_r_badkey); + cp += n; + + /* Verify that the key used is OK. */ + if ((ns_samename(state->key->dk_key_name, name) != 1 || + state->key->dk_alg != KEY_HMAC_MD5)) + return (-ns_r_badkey); + + /* Read the time signed and fudge. */ + BOUNDS_CHECK(cp, NS_INT16SZ + NS_INT32SZ + NS_INT16SZ); + cp += NS_INT16SZ; + NS_GET32(timesigned, cp); + NS_GET16(fudge, cp); + + /* Read the signature. */ + BOUNDS_CHECK(cp, NS_INT16SZ); + NS_GET16(sigfieldlen, cp); + BOUNDS_CHECK(cp, sigfieldlen); + sigstart = cp; + cp += sigfieldlen; + + /* Read the original id and error. */ + BOUNDS_CHECK(cp, 2*NS_INT16SZ); + NS_GET16(id, cp); + NS_GET16(error, cp); + + /* Parse the other data. */ + BOUNDS_CHECK(cp, NS_INT16SZ); + NS_GET16(otherfieldlen, cp); + BOUNDS_CHECK(cp, otherfieldlen); + cp += otherfieldlen; + + if (cp != eom) + return (NS_TSIG_ERROR_FORMERR); + + /* + * Do the verification. + */ + + /* Digest the time signed and fudge. */ + cp2 = buf; + NS_PUT16(0, cp2); /* Top 16 bits of time. */ + NS_PUT32(timesigned, cp2); + NS_PUT16(NS_TSIG_FUDGE, cp2); + + dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx, + buf, cp2 - buf, NULL, 0); + + n = dst_verify_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0, + sigstart, sigfieldlen); + if (n < 0) + return (-ns_r_badsig); + + if (sigfieldlen > sizeof(state->sig)) + return (NS_TSIG_ERROR_NO_SPACE); + + memcpy(state->sig, sigstart, sigfieldlen); + state->siglen = sigfieldlen; + + /* Verify the time. */ + if (abs(timesigned - time(NULL)) > fudge) + return (-ns_r_badtime); + + *msglen = recstart - msg; + + if (error != ns_r_noerror) + return (error); + + return (0); +} diff --git a/res_comp.c b/res_comp.c new file mode 100644 index 0000000..2b44cca --- /dev/null +++ b/res_comp.c @@ -0,0 +1,262 @@ +/* + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)res_comp.c 8.1 (Berkeley) 6/4/93"; +static const char rcsid[] = "$Id: res_comp.c,v 1.1 2006/03/01 19:01:37 majka Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#ifndef __APPLE__ +#include "port_before.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef __APPLE__ +#include "port_after.h" +#endif + +/* + * Expand compressed domain name 'comp_dn' to full domain name. + * 'msg' is a pointer to the begining of the message, + * 'eomorig' points to the first location after the message, + * 'exp_dn' is a pointer to a buffer of size 'length' for the result. + * Return size of compressed name or -1 if there was an error. + */ +int +dn_expand(const u_char *msg, const u_char *eom, const u_char *src, + char *dst, int dstsiz) +{ + int n = ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz); + + if (n > 0 && dst[0] == '.') + dst[0] = '\0'; + return (n); +} + +/* + * Pack domain name 'exp_dn' in presentation form into 'comp_dn'. + * Return the size of the compressed name or -1. + * 'length' is the size of the array pointed to by 'comp_dn'. + */ +int +dn_comp(const char *src, u_char *dst, int dstsiz, + u_char **dnptrs, u_char **lastdnptr) +{ + return (ns_name_compress(src, dst, (size_t)dstsiz, + (const u_char **)dnptrs, + (const u_char **)lastdnptr)); +} + +/* + * Skip over a compressed domain name. Return the size or -1. + */ +int +dn_skipname(const u_char *ptr, const u_char *eom) { + const u_char *saveptr = ptr; + + if (ns_name_skip(&ptr, eom) == -1) + return (-1); + return (ptr - saveptr); +} + +/* + * Verify that a domain name uses an acceptable character set. + */ + +/* + * Note the conspicuous absence of ctype macros in these definitions. On + * non-ASCII hosts, we can't depend on string literals or ctype macros to + * tell us anything about network-format data. The rest of the BIND system + * is not careful about this, but for some reason, we're doing it right here. + */ +#define PERIOD 0x2e +#define hyphenchar(c) ((c) == 0x2d) +#define bslashchar(c) ((c) == 0x5c) +#define periodchar(c) ((c) == PERIOD) +#define asterchar(c) ((c) == 0x2a) +#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \ + || ((c) >= 0x61 && (c) <= 0x7a)) +#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) + +#define borderchar(c) (alphachar(c) || digitchar(c)) +#define middlechar(c) (borderchar(c) || hyphenchar(c)) +#define domainchar(c) ((c) > 0x20 && (c) < 0x7f) + +int +res_hnok(const char *dn) { + int ppch = '\0', pch = PERIOD, ch = *dn++; + + while (ch != '\0') { + int nch = *dn++; + + if (periodchar(ch)) { + (void)NULL; + } else if (periodchar(pch)) { + if (!borderchar(ch)) + return (0); + } else if (periodchar(nch) || nch == '\0') { + if (!borderchar(ch)) + return (0); + } else { + if (!middlechar(ch)) + return (0); + } + ppch = pch, pch = ch, ch = nch; + } + return (1); +} + +/* + * hostname-like (A, MX, WKS) owners can have "*" as their first label + * but must otherwise be as a host name. + */ +int +res_ownok(const char *dn) { + if (asterchar(dn[0])) { + if (periodchar(dn[1])) + return (res_hnok(dn+2)); + if (dn[1] == '\0') + return (1); + } + return (res_hnok(dn)); +} + +/* + * SOA RNAMEs and RP RNAMEs can have any printable character in their first + * label, but the rest of the name has to look like a host name. + */ +int +res_mailok(const char *dn) { + int ch, escaped = 0; + + /* "." is a valid missing representation */ + if (*dn == '\0') + return (1); + + /* otherwise