From 8a97ab44e35a0ae5457203472875cb92bc11e419 Mon Sep 17 00:00:00 2001 From: Apple Date: Thu, 11 Oct 2007 00:27:09 +0000 Subject: [PATCH 1/1] libresolv-19.tar.gz --- Makefile | 64 ++ Makefile.postamble | 31 + Makefile.preamble | 8 + PB.project | 82 ++ base64.c | 326 +++++++ dns.c | 1606 +++++++++++++++++++++++++++++++++ dns.h | 139 +++ dns_async.c | 304 +++++++ dns_private.h | 87 ++ dns_util.c | 2150 ++++++++++++++++++++++++++++++++++++++++++++ dns_util.h | 353 ++++++++ dst.h | 156 ++++ dst_api.c | 1076 ++++++++++++++++++++++ dst_hmac_link.c | 481 ++++++++++ dst_internal.h | 188 ++++ dst_support.c | 367 ++++++++ nameser.h | 588 ++++++++++++ ns_date.c | 134 +++ ns_name.c | 949 +++++++++++++++++++ ns_netint.c | 62 ++ ns_parse.c | 208 +++++ ns_print.c | 914 +++++++++++++++++++ ns_samedomain.c | 212 +++++ ns_sign.c | 377 ++++++++ ns_ttl.c | 165 ++++ ns_verify.c | 488 ++++++++++ res_comp.c | 262 ++++++ res_data.c | 327 +++++++ res_debug.c | 1143 +++++++++++++++++++++++ res_debug.h | 34 + res_findzonecut.c | 702 +++++++++++++++ res_init.c | 1562 ++++++++++++++++++++++++++++++++ res_mkquery.c | 271 ++++++ res_mkupdate.c | 1124 +++++++++++++++++++++++ res_private.h | 101 +++ res_query.c | 987 ++++++++++++++++++++ res_send.c | 1435 +++++++++++++++++++++++++++++ res_sendsigned.c | 142 +++ res_update.c | 211 +++++ res_update.h | 117 +++ resolv.h | 499 ++++++++++ resolver.3 | 422 +++++++++ resolver.5 | 225 +++++ resolver_so.3 | 1 + 44 files changed, 21080 insertions(+) create mode 100644 Makefile create mode 100644 Makefile.postamble create mode 100644 Makefile.preamble create mode 100644 PB.project create mode 100644 base64.c create mode 100644 dns.c create mode 100644 dns.h create mode 100644 dns_async.c create mode 100644 dns_private.h create mode 100644 dns_util.c create mode 100644 dns_util.h create mode 100644 dst.h create mode 100644 dst_api.c create mode 100644 dst_hmac_link.c create mode 100644 dst_internal.h create mode 100644 dst_support.c create mode 100644 nameser.h create mode 100644 ns_date.c create mode 100644 ns_name.c create mode 100644 ns_netint.c create mode 100644 ns_parse.c create mode 100644 ns_print.c create mode 100644 ns_samedomain.c create mode 100644 ns_sign.c create mode 100644 ns_ttl.c create mode 100644 ns_verify.c create mode 100644 res_comp.c create mode 100644 res_data.c create mode 100644 res_debug.c create mode 100644 res_debug.h create mode 100644 res_findzonecut.c create mode 100644 res_init.c create mode 100644 res_mkquery.c create mode 100644 res_mkupdate.c create mode 100644 res_private.h create mode 100644 res_query.c create mode 100644 res_send.c create mode 100644 res_sendsigned.c create mode 100644 res_update.c create mode 100644 res_update.h create mode 100644 resolv.h create mode 100644 resolver.3 create mode 100644 resolver.5 create mode 100644 resolver_so.3 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