diff options
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/nsd/config.h.in | 52 | ||||
-rw-r--r-- | usr.sbin/nsd/configure.ac | 21 | ||||
-rw-r--r-- | usr.sbin/nsd/dname.c | 83 | ||||
-rw-r--r-- | usr.sbin/nsd/dns.h | 7 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-checkconf.8.in | 2 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-checkconf.c | 3 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-notify.8.in | 2 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-patch.8.in | 4 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-xfer.8.in | 2 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.8.in | 4 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.c | 9 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.conf.5.in | 8 | ||||
-rw-r--r-- | usr.sbin/nsd/nsdc.8.in | 2 | ||||
-rw-r--r-- | usr.sbin/nsd/nsdc.sh.in | 10 | ||||
-rw-r--r-- | usr.sbin/nsd/nsec3.c | 504 | ||||
-rw-r--r-- | usr.sbin/nsd/query.c | 28 | ||||
-rw-r--r-- | usr.sbin/nsd/rdata.c | 21 | ||||
-rw-r--r-- | usr.sbin/nsd/server.c | 22 | ||||
-rw-r--r-- | usr.sbin/nsd/zonec.8.in | 4 | ||||
-rw-r--r-- | usr.sbin/nsd/zonec.c | 48 |
20 files changed, 734 insertions, 102 deletions
diff --git a/usr.sbin/nsd/config.h.in b/usr.sbin/nsd/config.h.in index 19d891b2e79..6fd9a2dd2b4 100644 --- a/usr.sbin/nsd/config.h.in +++ b/usr.sbin/nsd/config.h.in @@ -25,6 +25,9 @@ /* Define to the default facility for syslog. */ #undef FACILITY +/* Define this to enable NSEC3 full prehashing. */ +#undef FULL_PREHASH + /* Define to 1 if you have the `alarm' function. */ #undef HAVE_ALARM @@ -305,6 +308,9 @@ /* Define if memcmp() does not compare unsigned bytes */ #undef MEMCMP_IS_BROKEN +/* Define this to enable response minimalization to reduce truncation. */ +#undef MINIMAL_RESPONSES + /* Undefine this to enable internal runtime checks. */ #undef NDEBUG @@ -323,6 +329,9 @@ /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME +/* Define to the home page for this package. */ +#undef PACKAGE_URL + /* Define to the version of this package. */ #undef PACKAGE_VERSION @@ -368,6 +377,28 @@ /* Define this to enable mmap instead of malloc. Experimental. */ #undef USE_MMAP_ALLOC +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + /* Define to the NSD version to answer version.server query. */ #undef VERSION @@ -381,13 +412,6 @@ /* NSD default location for zone files. Empty string or NULL to disable. */ #undef ZONESDIR -/* Define to 1 if on AIX 3. - System headers sometimes define this. - We just want to avoid a redefinition error message. */ -#ifndef _ALL_SOURCE -# undef _ALL_SOURCE -#endif - /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS @@ -397,6 +421,16 @@ /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + /* Define to empty if `const' does not conform to ANSI C. */ #undef const @@ -427,7 +461,7 @@ /* Define to rpl_malloc if the replacement function should be used. */ #undef malloc -/* Define to `long' if <sys/types.h> does not define. */ +/* Define to `long int' if <sys/types.h> does not define. */ #undef off_t /* Define to `int' if <sys/types.h> does not define. */ @@ -436,7 +470,7 @@ /* Define "sig_atomic_t" to "int" if "sig_atomic_t" is missing */ #undef sig_atomic_t -/* Define to `unsigned' if <sys/types.h> does not define. */ +/* Define to `unsigned int' if <sys/types.h> does not define. */ #undef size_t /* Define "socklen_t" to "int" if "socklen_t" is missing */ diff --git a/usr.sbin/nsd/configure.ac b/usr.sbin/nsd/configure.ac index bfb79f8ef17..79087e654bc 100644 --- a/usr.sbin/nsd/configure.ac +++ b/usr.sbin/nsd/configure.ac @@ -4,7 +4,7 @@ dnl sinclude(acx_nlnetlabs.m4) -AC_INIT(NSD,3.2.8,nsd-bugs@nlnetlabs.nl) +AC_INIT(NSD,3.2.9,nsd-bugs@nlnetlabs.nl) AC_CONFIG_HEADER([config.h]) AC_AIX @@ -65,6 +65,7 @@ AC_SUBST(kill_priority) # Default logfile # logfile=${localstatedir}/log/nsd.log +AC_SUBST(logfile) # # Database directory @@ -609,6 +610,24 @@ case "$enable_nsec3" in ;; esac +AC_ARG_ENABLE(full-prehash, AC_HELP_STRING([--disable-full-prehash], [Disables NSEC3 full prehashing])) +case "$enable_full_prehash" in + no) + ;; + yes|*) + AC_DEFINE_UNQUOTED([FULL_PREHASH], [], [Define this to enable NSEC3 full prehashing.]) + ;; +esac + +AC_ARG_ENABLE(minimal-responses, AC_HELP_STRING([--disable-minimal-responses], [Disable response minimization. More truncation.])) +case "$enable_minimal_responses" in + no) + ;; + yes|*) + AC_DEFINE_UNQUOTED([MINIMAL_RESPONSES], [], [Define this to enable response minimalization to reduce truncation.]) + ;; +esac + AC_ARG_ENABLE(mmap, AC_HELP_STRING([--enable-mmap], [Use mmap instead of malloc. Experimental.])) case "$enable_mmap" in yes) diff --git a/usr.sbin/nsd/dname.c b/usr.sbin/nsd/dname.c index cc3c6a2a89c..03d3c624cfd 100644 --- a/usr.sbin/nsd/dname.c +++ b/usr.sbin/nsd/dname.c @@ -17,11 +17,19 @@ #include <limits.h> #include <stdio.h> #include <string.h> +#include <errno.h> #include "dns.h" #include "dname.h" #include "query.h" +/** + * The maximum number of labels is the maximum domain name, less 1 for + * the root (len == 0) label, divided by two (minimum non-root label of + * one character + length byte), then add back in one for the root label. + */ +#define MAXLABELS (((MAXDOMAINLEN - 1) / 2) + 1) + const dname_type * dname_make(region_type *region, const uint8_t *name, int normalize) { @@ -301,8 +309,10 @@ dname_is_subdomain(const dname_type *left, const dname_type *right) int -dname_compare(const dname_type *left, const dname_type *right) +dname_compare(const void *vleft, const void *vright) { + const dname_type* left = (const dname_type*) vleft; + const dname_type* right = (const dname_type*) vright; int result; uint8_t label_count; uint8_t i; @@ -382,16 +392,27 @@ const char * dname_to_string(const dname_type *dname, const dname_type *origin) { static char buf[MAXDOMAINLEN * 5]; + return dname_to_string_r(dname, origin, buf); +} + +const char * +dname_to_string_r(const dname_type *dname, const dname_type *origin, + char* buf) +{ size_t i; - size_t labels_to_convert = dname->label_count - 1; + size_t labels_to_convert = 0; int absolute = 1; char *dst; const uint8_t *src; - + if (!dname) { + *buf = '\0'; + return buf; + } if (dname->label_count == 1) { strlcpy(buf, ".", sizeof(buf)); return buf; } + labels_to_convert = dname->label_count - 1; if (origin && dname_is_subdomain(dname, origin)) { int common_labels = dname_label_match_count(dname, origin); @@ -495,3 +516,59 @@ dname_replace(region_type* region, return res; } +#ifndef FULL_PREHASH +/** + * Make wildcard synthesis. + * + */ +int +dname_make_wildcard(struct region *region, + struct dname const *dname, + struct dname const **wildcard) +{ + uint8_t name_size; + uint8_t label_count; + uint8_t *names; + uint8_t *labels; + struct dname *new_dname; + unsigned int i; + /* + * Checks: + * dname label_count + 1 < MAXLABELS + * dname size + 2 < MAXDOMAINLEN + */ + if (dname->label_count > (MAXLABELS - 1)) { + return EINVAL; + } + if (dname->name_size > (MAXDOMAINLEN - 2)) { + return EINVAL; + } + + label_count = dname->label_count + 1; + name_size = dname->name_size + 2; + new_dname = (struct dname *) region_alloc(region, + sizeof(dname_type) + + (label_count * sizeof(uint8_t)) + + (name_size * sizeof(uint8_t))); + if (new_dname == NULL) { + return ENOMEM; + } + new_dname->label_count = label_count; + new_dname->name_size = name_size; + labels = (uint8_t *) dname_label_offsets(new_dname); + memcpy(labels, dname_label_offsets(dname), + dname->label_count * sizeof(uint8_t)); + for (i = 0; i < dname->label_count; i++) { + labels[i] += 2; + } + labels[i] = 0; + + names = (uint8_t *) dname_name(new_dname); + *names++ = '\001'; + *names++ = '*'; + memcpy(names, dname_name(dname), + dname->name_size * sizeof(uint8_t)); + *wildcard = new_dname; + return 0; +} +#endif
\ No newline at end of file diff --git a/usr.sbin/nsd/dns.h b/usr.sbin/nsd/dns.h index dc0877ac314..ce1d019531d 100644 --- a/usr.sbin/nsd/dns.h +++ b/usr.sbin/nsd/dns.h @@ -14,6 +14,11 @@ enum rr_section { QUESTION_SECTION, ANSWER_SECTION, AUTHORITY_SECTION, + /* + * Use a split authority section to ensure that optional + * NS RRsets in the response can be omitted. + */ + OPTIONAL_AUTHORITY_SECTION, ADDITIONAL_SECTION, /* * Use a split additional section to ensure A records appear @@ -146,7 +151,7 @@ typedef enum nsd_rc nsd_rc_type; #define MAXLABELLEN 63 #define MAXDOMAINLEN 255 -#define MAXRDATALEN 64 /* This is more than enough, think multiple TXT. */ +#define MAXRDATALEN 64 /* This is more than enough, think multiple TXT. */ #define MAX_RDLENGTH 65535 /* Maximum size of a single RR. */ diff --git a/usr.sbin/nsd/nsd-checkconf.8.in b/usr.sbin/nsd/nsd-checkconf.8.in index 05135fc871f..b769bd63678 100644 --- a/usr.sbin/nsd/nsd-checkconf.8.in +++ b/usr.sbin/nsd/nsd-checkconf.8.in @@ -1,4 +1,4 @@ -.TH "nsd\-checkconf" "8" "Mar 22, 2011" "NLnet Labs" "nsd 3.2.8" +.TH "nsd\-checkconf" "8" "Nov 23, 2011" "NLnet Labs" "nsd 3.2.9" .\" Copyright (c) 2001\-2011, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" diff --git a/usr.sbin/nsd/nsd-checkconf.c b/usr.sbin/nsd/nsd-checkconf.c index dc959257b37..2b086aed4e5 100644 --- a/usr.sbin/nsd/nsd-checkconf.c +++ b/usr.sbin/nsd/nsd-checkconf.c @@ -403,7 +403,7 @@ additional_checks(nsd_options_t* opt, const char* filename) fprintf(stderr, "%s: cannot base64 decode tsig secret: for key %s.\n", filename, key->name); errors ++; } - if(tsig_get_algorithm_by_name(key->algorithm) != NULL) + if(tsig_get_algorithm_by_name(key->algorithm) == NULL) { fprintf(stderr, "%s: bad tsig algorithm %s: for key \ %s.\n", filename, key->algorithm, key->name); @@ -528,6 +528,7 @@ main(int argc, char* argv[]) /* read config file */ options = nsd_options_create(region_create(xalloc, free)); + tsig_init(options->region); if (!parse_options_file(options, configfile) || !additional_checks(options, configfile)) { exit(2); diff --git a/usr.sbin/nsd/nsd-notify.8.in b/usr.sbin/nsd/nsd-notify.8.in index 8add6c35ec8..c60e751457a 100644 --- a/usr.sbin/nsd/nsd-notify.8.in +++ b/usr.sbin/nsd/nsd-notify.8.in @@ -1,4 +1,4 @@ -.TH "nsd\-notify" "8" "Mar 22, 2011" "NLnet Labs" "nsd 3.2.8" +.TH "nsd\-notify" "8" "Nov 23, 2011" "NLnet Labs" "nsd 3.2.9" .\" Copyright (c) 2001\-2011, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" diff --git a/usr.sbin/nsd/nsd-patch.8.in b/usr.sbin/nsd/nsd-patch.8.in index 3ab96da229d..4237c4bbf9d 100644 --- a/usr.sbin/nsd/nsd-patch.8.in +++ b/usr.sbin/nsd/nsd-patch.8.in @@ -1,10 +1,10 @@ -.TH "nsd\-patch" "8" "Mar 22, 2011" "NLnet Labs" "nsd 3.2.8" +.TH "nsd\-patch" "8" "Nov 23, 2011" "NLnet Labs" "nsd 3.2.9" .\" Copyright (c) 2001\-2011, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" .LP .B nsd\-patch -\- NSD zone patcher version 3.2.8. +\- NSD zone patcher version 3.2.9. .SH "SYNOPSIS" .B nsd\-patch .RB [ \-c diff --git a/usr.sbin/nsd/nsd-xfer.8.in b/usr.sbin/nsd/nsd-xfer.8.in index eb440a35b91..5fe0757c0df 100644 --- a/usr.sbin/nsd/nsd-xfer.8.in +++ b/usr.sbin/nsd/nsd-xfer.8.in @@ -1,4 +1,4 @@ -.TH "nsd\-xfer" "8" "Mar 22, 2011" "NLnet Labs" "nsd 3.2.8" +.TH "nsd\-xfer" "8" "Nov 23, 2011" "NLnet Labs" "nsd 3.2.9" .\" Copyright (c) 2001\-2011, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" diff --git a/usr.sbin/nsd/nsd.8.in b/usr.sbin/nsd/nsd.8.in index bee7c77718e..71088291df2 100644 --- a/usr.sbin/nsd/nsd.8.in +++ b/usr.sbin/nsd/nsd.8.in @@ -1,10 +1,10 @@ -.TH "NSD" "8" "Mar 22, 2011" "NLnet Labs" "NSD 3.2.8" +.TH "NSD" "8" "Nov 23, 2011" "NLnet Labs" "NSD 3.2.9" .\" Copyright (c) 2001\-2011, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" .LP .B nsd -\- Name Server Daemon (NSD) version 3.2.8. +\- Name Server Daemon (NSD) version 3.2.9. .SH "SYNOPSIS" .LP .B nsd diff --git a/usr.sbin/nsd/nsd.c b/usr.sbin/nsd/nsd.c index 372c2b47f3f..9b1f5eeff48 100644 --- a/usr.sbin/nsd/nsd.c +++ b/usr.sbin/nsd/nsd.c @@ -490,7 +490,7 @@ main(int argc, char *argv[]) if (hex_pton(optarg, nsd.nsid, nsd.nsid_len) == -1) { error("hex string cannot be parsed '%s' in NSID.", optarg); } - break; + break; case 'l': nsd.log_filename = optarg; break; @@ -863,8 +863,11 @@ main(int argc, char *argv[]) log_open(LOG_PID, FACILITY, nsd.log_filename); if (!nsd.log_filename) log_set_log_function(log_syslog); - else if (nsd.uid && nsd.gid) - (void) chown(nsd.log_filename, nsd.uid, nsd.gid); + else if (nsd.uid && nsd.gid) { + if(chown(nsd.log_filename, nsd.uid, nsd.gid) != 0) + VERBOSITY(2, (LOG_WARNING, "chown %s failed: %s", + nsd.log_filename, strerror(errno))); + } /* Do we have a running nsd? */ if ((oldpid = readpid(nsd.pidfile)) == -1) { diff --git a/usr.sbin/nsd/nsd.conf.5.in b/usr.sbin/nsd/nsd.conf.5.in index a2d8ee5ded5..b062ff4ace6 100644 --- a/usr.sbin/nsd/nsd.conf.5.in +++ b/usr.sbin/nsd/nsd.conf.5.in @@ -1,4 +1,4 @@ -.TH "nsd.conf" "5" "Mar 22, 2011" "NLnet Labs" "nsd 3.2.8" +.TH "nsd.conf" "5" "Nov 23, 2011" "NLnet Labs" "nsd 3.2.9" .\" Copyright (c) 2001\-2011, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" @@ -217,7 +217,7 @@ gone. For more details see the section on zone expiry behavior of NSD. Default is .IR @xfrdfile@ . .TP -.B xrfd\-reload\-timeout:\fR <number> +.B xfrd\-reload\-timeout:\fR <number> If this value is \-1, xfrd will not trigger a reload after a zone transfer. If positive xfrd will trigger a reload after a zone transfer, then it will wait for the number of seconds before it will @@ -350,7 +350,7 @@ file format, named.conf(5). BIND9 types zones as 'Master' or 'Slave'. .SS "Slave zones" For a slave zone, the master servers are listed. The master servers are queried for zone data, and are listened to for update notifications. -In NSD these two properties need to be configured seperately, by listing +In NSD these two properties need to be configured separately, by listing the master address in allow\-notify and request\-xfr statements. .P In BIND9 you only need to provide allow\-notify elements for @@ -444,7 +444,7 @@ that are also allowed to send notifications to this slave server. For a master zone in BIND9, the slave servers are listed. These slave servers are sent notifications of updated and are allowed to request transfer of the zone data. In NSD these two properties need to be -configured seperately. +configured separately. .P Here is an example of a master zone in BIND9 syntax. .LP diff --git a/usr.sbin/nsd/nsdc.8.in b/usr.sbin/nsd/nsdc.8.in index 5c7e109df9e..9b6d6a0015a 100644 --- a/usr.sbin/nsd/nsdc.8.in +++ b/usr.sbin/nsd/nsdc.8.in @@ -1,4 +1,4 @@ -.TH "NSDC" "8" "Mar 22, 2011" "NLnet Labs" "NSDC 3.2.8" +.TH "NSDC" "8" "Nov 23, 2011" "NLnet Labs" "NSDC 3.2.9" .\" Copyright (c) 2001\-2011, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" diff --git a/usr.sbin/nsd/nsdc.sh.in b/usr.sbin/nsd/nsdc.sh.in index 1d849563457..79b6445a269 100644 --- a/usr.sbin/nsd/nsdc.sh.in +++ b/usr.sbin/nsd/nsdc.sh.in @@ -17,11 +17,11 @@ configfile="@nsdconfigfile@" # The directory where NSD binaries reside sbindir="@sbindir@" -# how verbose is nsd-zonec run. Specify Nothing (empty string), -v or -vv. -ZONEC_VERBOSE= +# how verbose is zonec run. Specify Nothing " ", -v or -vv. +NSDC_ZONEC_VERBOSE=${NSDC_ZONEC_VERBOSE:--v} # how patch is done. Specify 1 (with use of textfiles, default) or 0 (without) -PATCH_STYLE=1 +NSDC_PATCH_STYLE=${NSDC_PATCH_STYLE:-1} # # You sure heard this many times before: NO USER SERVICEABLE PARTS BELOW @@ -335,7 +335,7 @@ do_patch() { do_rebuild() { lock && \ - ${sbindir}/nsd-zonec ${ZONEC_VERBOSE} -c ${configfile} -f ${dbfile}.$$ && \ + ${sbindir}/nsd-zonec ${NSDC_ZONEC_VERBOSE} -c ${configfile} -f ${dbfile}.$$ && \ mv ${dbfile}.$$ ${dbfile} result=$? unlock @@ -370,7 +370,7 @@ patch) if test -s ${difffile}; then #${sbindir}/nsd-patch -c ${configfile} -x ${difffile} -l #debug #echo ${sbindir}/nsd-patch -c ${configfile} -x ${difffile} - if do_patch ${PATCH_STYLE}; then + if do_patch ${NSDC_PATCH_STYLE}; then do_reload else unlock diff --git a/usr.sbin/nsd/nsec3.c b/usr.sbin/nsd/nsec3.c index ac2b3df6a01..dc1feadb430 100644 --- a/usr.sbin/nsd/nsec3.c +++ b/usr.sbin/nsd/nsec3.c @@ -10,6 +10,7 @@ #ifdef NSEC3 #include <stdio.h> #include <stdlib.h> +#include <errno.h> #include "nsec3.h" #include "iterated_hash.h" @@ -19,10 +20,6 @@ #define NSEC3_SHA1_HASH 1 /* same type code as DS hash */ -/* detect is the latter rrset has the same hashalgo, iterations and salt - as the base. Does not compare optout bit, or other rdata. - base=NULL uses the zone soa_rr. */ -static int nsec3_rrset_params_ok(rr_type* base, rrset_type* rrset); static void detect_nsec3_params(rr_type* nsec3_apex, @@ -86,14 +83,15 @@ find_zone_nsec3(namedb_type* namedb, zone_type *zone) must map to the zone apex. */ rrset_type* paramset = domain_find_rrset(zone->apex, zone, TYPE_NSEC3PARAM); if(!paramset || !paramset->rrs || !paramset->rr_count) - return 0; + return NULL; tmpregion = region_create(xalloc, free); - for(i=0; i<paramset->rr_count; i++) - { + for(i=0; i < paramset->rr_count; i++) { rr_type* rr = ¶mset->rrs[i]; const dname_type* hashed_apex; rrset_type* nsec3_rrset; size_t j; + const unsigned char *salt1; + int saltlen1, iter1; if(rdata_atom_data(rr->rdatas[0])[0] != NSEC3_SHA1_HASH) { log_msg(LOG_ERR, "%s NSEC3PARAM entry %d has unknown hash algo %d", @@ -123,20 +121,19 @@ find_zone_nsec3(namedb_type* namedb, zone_type *zone) dname_to_string(domain_dname(zone->apex), NULL), (int)i); continue; } + detect_nsec3_params(rr, &salt1, &saltlen1, &iter1); /* find SOA bit enabled nsec3, with the same settings */ - for(j=0; j<nsec3_rrset->rr_count; j++) - { - const unsigned char *salt1, *salt2; - int saltlen1, saltlen2, iter1, iter2; + for(j=0; j < nsec3_rrset->rr_count; j++) { + const unsigned char *salt2; + int saltlen2, iter2; if(!nsec3_has_soa(&nsec3_rrset->rrs[j])) continue; /* check params OK. Ignores the optout bit. */ - detect_nsec3_params(rr, &salt1, &saltlen1, &iter1); detect_nsec3_params(&nsec3_rrset->rrs[j], &salt2, &saltlen2, &iter2); if(saltlen1 == saltlen2 && iter1 == iter2 && rdata_atom_data(rr->rdatas[0])[0] == /* algo */ - rdata_atom_data(nsec3_rrset->rrs[j].rdatas[0])[0] + rdata_atom_data(nsec3_rrset->rrs[j].rdatas[0])[0] && memcmp(salt1, salt2, saltlen1) == 0) { /* found it */ DEBUG(DEBUG_QUERY, 1, (LOG_INFO, @@ -151,7 +148,7 @@ find_zone_nsec3(namedb_type* namedb, zone_type *zone) dname_to_string(domain_dname(zone->apex), NULL), (int)i); } region_destroy(tmpregion); - return 0; + return NULL; } /* check that the rrset has an NSEC3 that uses the same parameters as the @@ -161,7 +158,6 @@ static int nsec3_rrset_params_ok(rr_type* base, rrset_type* rrset) { rdata_atom_type* prd; - rdata_atom_type* rd; size_t i; if(!rrset) return 0; /* without rrset, no matching params either */ @@ -169,9 +165,8 @@ nsec3_rrset_params_ok(rr_type* base, rrset_type* rrset) if(!base) base = rrset->zone->nsec3_soa_rr; prd = base->rdatas; - for(i=0; i<rrset->rr_count; ++i) - { - rd = rrset->rrs[i].rdatas; + for(i=0; i < rrset->rr_count; ++i) { + rdata_atom_type* rd = rrset->rrs[i].rdatas; assert(rrset->rrs[i].type == TYPE_NSEC3); if(rdata_atom_data(rd[0])[0] == rdata_atom_data(prd[0])[0] && /* hash algo */ @@ -192,6 +187,8 @@ nsec3_rrset_params_ok(rr_type* base, rrset_type* rrset) return 0; } +#ifdef FULL_PREHASH + int nsec3_find_cover(namedb_type* db, zone_type* zone, const dname_type* hashname, domain_type** result) @@ -247,15 +244,62 @@ nsec3_find_cover(namedb_type* db, zone_type* zone, return 0; } +#else + +int +nsec3_find_cover(namedb_type* ATTR_UNUSED(db), zone_type* zone, + const dname_type* hashname, struct nsec3_domain **result) +{ + rbnode_t *node; + int exact; + + assert(result); + if (!zone->nsec3_domains) + return 0; + + exact = rbtree_find_less_equal(zone->nsec3_domains, hashname, &node); + if (!node) { + exact = 0; + node = rbtree_last(zone->nsec3_domains); + } + + while (node != RBTREE_NULL) { + struct rrset *nsec3_rrset; + struct nsec3_domain *nsec3_domain = + (struct nsec3_domain *) node; + nsec3_rrset = domain_find_rrset(nsec3_domain->nsec3_domain, + zone, TYPE_NSEC3); + if (!nsec3_rrset) { + /* + * RRset in zone->nsec3_domains whose type != NSEC3 + * If we get here, something is seriously wrong! + */ + return 0; + } + if (nsec3_rrset_params_ok(NULL, nsec3_rrset) != 0) { + *result = nsec3_domain; + return exact; + } + exact = 0; /* No match, so we're looking for closest match */ + node = rbtree_previous(node); + } + /* + * If we reach this point, *result == NULL. This should + * never happen since the zone should have one NSEC3 record with + * the SOA bit set, which matches a NSEC3PARAM RR in the zone. + */ + return exact; +} +#endif + +#ifdef FULL_PREHASH static void -prehash_domain(namedb_type* db, zone_type* zone, +prehash_domain_r(namedb_type* db, zone_type* zone, domain_type* domain, region_type* region) { - /* find it */ - domain_type* result = 0; - const dname_type *wcard, *wcard_child, *hashname; int exact; - + const dname_type *wcard, *wcard_child, *hashname; + domain_type* result = 0; if(!zone->nsec3_soa_rr) { /* set to 0 (in case NSEC3 removed after an update) */ @@ -293,6 +337,36 @@ prehash_domain(namedb_type* db, zone_type* zone, } } +#else + +static void +prehash_domain_r(namedb_type* db, zone_type* zone, + domain_type* domain, region_type* region) +{ + int exact; + const dname_type *hashname; + struct nsec3_domain* result = NULL; + + domain->nsec3_cover = NULL; + hashname = nsec3_hash_dname(region, zone, domain_dname(domain)); + exact = nsec3_find_cover(db, zone, hashname, &result); + if (result && exact) + { + result->covers = domain; + domain->nsec3_cover = result->nsec3_domain; + } + return; +} +#endif + +static void +prehash_domain(namedb_type* db, zone_type* zone, + domain_type* domain, region_type* region) +{ + prehash_domain_r(db, zone, domain, region); +} + +#ifdef FULL_PREHASH static void prehash_ds(namedb_type* db, zone_type* zone, domain_type* domain, region_type* region) @@ -315,8 +389,105 @@ prehash_ds(namedb_type* db, zone_type* zone, else domain->nsec3_ds_parent_is_exact = 0; domain->nsec3_ds_parent_cover = result; } +#endif -static void +#ifndef FULL_PREHASH +struct domain * +find_last_nsec3_domain(struct zone *zone) +{ + rbnode_t *node; + if (zone->nsec3_domains == NULL) { + return NULL; + } + node = rbtree_last(zone->nsec3_domains); + if (node == RBTREE_NULL) { + return NULL; + } + return ((struct nsec3_domain *) node)->nsec3_domain; +} + +void +prehash_zone_incremental(struct namedb *db, struct zone *zone) +{ + region_type *temp_region; + rbnode_t *node; + /* find zone NSEC3PARAM settings */ + zone->nsec3_soa_rr = find_zone_nsec3(db, zone); + if (zone->nsec3_soa_rr == NULL) { + zone->nsec3_last = NULL; + return; + } + if (db->nsec3_mod_domains == NULL) { + return; + } + zone->nsec3_last = find_last_nsec3_domain(zone); + temp_region = region_create(xalloc, free); + node = rbtree_first(db->nsec3_mod_domains); + while (node != RBTREE_NULL) { + struct nsec3_mod_domain *nsec3_mod_domain = + (struct nsec3_mod_domain *) node; + struct domain *walk = nsec3_mod_domain->domain; + struct domain *domain_zone_apex; + + if (!walk || + (dname_is_subdomain(domain_dname(walk), + domain_dname(zone->apex)) == 0)) { + node = rbtree_next(node); + continue; + } + if (!walk->nsec3_cover) { + node = rbtree_next(node); + continue; + } + /* Empty Terminal */ + if (!walk->is_existing) { + walk->nsec3_cover = NULL; + node = rbtree_next(node); + continue; + } + + /* + * Don't hash NSEC3 only nodes, unless possibly + * part of a weird case where node is empty nonterminal + * requiring NSEC3 but node name also is the hashed + * node name of another node requiring NSEC3. + * NSEC3 Empty Nonterminal with NSEC3 RRset present. + */ + if (domain_has_only_NSEC3(walk, zone) != 0) { + struct domain *next_domain = domain_next(walk); + if ((next_domain == NULL) || + (next_domain->parent != walk)) { + walk->nsec3_cover = NULL; + node = rbtree_next(node); + continue; + } + } + + /* + * Identify domain nodes that belong to the zone + * which are not glue records. What if you hit a + * record that's in two zones but which has no + * cut point between the zones. Not valid but + * someone is gonna try it sometime. + * This implementation doesn't link an NSEC3 + * record to the domain. + */ + domain_zone_apex = domain_find_zone_apex(walk); + if ((domain_zone_apex != NULL) && + (domain_zone_apex == zone->apex) && + (domain_is_glue(walk, zone) == 0)) { + + prehash_domain(db, zone, walk, temp_region); + region_free_all(temp_region); + } + + node = rbtree_next(node); + } + namedb_nsec3_mod_domains_destroy(db); + region_destroy(temp_region); +} + +void prehash_zone(struct namedb* db, struct zone* zone) { domain_type *walk; @@ -327,10 +498,87 @@ prehash_zone(struct namedb* db, struct zone* zone) /* find zone settings */ zone->nsec3_soa_rr = find_zone_nsec3(db, zone); if(!zone->nsec3_soa_rr) { - zone->nsec3_last = 0; + zone->nsec3_last = NULL; return; } + temp_region = region_create(xalloc, free); + + /* go through entire zone and setup nsec3_lookup speedup */ + walk = zone->apex; + last_nsec3_node = NULL; + /* since we walk in sorted order, we pass all NSEC3s in sorted + order and we can set the lookup ptrs */ + while(walk && dname_is_subdomain( + domain_dname(walk), domain_dname(zone->apex))) + { + struct domain *domain_zone_apex; + + if (walk->nsec3_cover != NULL) { + walk = domain_next(walk); + continue; + } + /* Empty Terminal */ + if (walk->is_existing == 0) { + walk->nsec3_cover = NULL; + walk = domain_next(walk); + continue; + } + /* + * Don't hash NSEC3 only nodes, unless possibly + * part of a weird case where node is empty nonterminal + * requiring NSEC3 but node name also is the hashed + * node name of another node requiring NSEC3. + * NSEC3 Empty Nonterminal with NSEC3 RRset present. + */ + if (domain_has_only_NSEC3(walk, zone)) { + struct domain *next_domain = domain_next(walk); + if ((next_domain == NULL) || + (next_domain->parent != walk)) { + walk->nsec3_cover = NULL; + walk = domain_next(walk); + continue; + } + } + + /* + * Identify domain nodes that belong to the zone + * which are not glue records. What if you hit a + * record that's in two zones but which has no + * cut point between the zones. Not valid but + * someone is gonna try it sometime. + * This implementation doesn't link an NSEC3 + * record to the domain. + */ + domain_zone_apex = domain_find_zone_apex(walk); + if ((domain_zone_apex != NULL) && + (domain_zone_apex == zone->apex) && + (domain_is_glue(walk, zone) == 0)) + { + prehash_domain(db, zone, walk, temp_region); + region_free_all(temp_region); + } + walk = domain_next(walk); + } + region_destroy(temp_region); +} + +#else + +static void +prehash_zone(struct namedb* db, struct zone* zone) +{ + domain_type *walk; + domain_type *last_nsec3_node; + region_type *temp_region; + assert(db && zone); + + /* find zone settings */ + zone->nsec3_soa_rr = find_zone_nsec3(db, zone); + if(!zone->nsec3_soa_rr) { + zone->nsec3_last = NULL; + return; + } temp_region = region_create(xalloc, free); /* go through entire zone and setup nsec3_lookup speedup */ @@ -358,7 +606,7 @@ prehash_zone(struct namedb* db, struct zone* zone) domain_dname(walk), domain_dname(zone->apex))) { zone_type* z; - if(!walk->is_existing || domain_has_only_NSEC3(walk, zone)) { + if(!walk->is_existing && domain_has_only_NSEC3(walk, zone)) { walk->nsec3_cover = NULL; walk->nsec3_wcard_child_cover = NULL; walk = domain_next(walk); @@ -383,6 +631,7 @@ prehash_zone(struct namedb* db, struct zone* zone) } region_destroy(temp_region); } +#endif void prehash(struct namedb* db, int updated_only) @@ -404,6 +653,53 @@ prehash(struct namedb* db, int updated_only) "seconds for %d zones.", (int)(end-start), count)); } + +#ifndef FULL_PREHASH +static void +nsec3_hash_and_find_cover(struct region *region, + struct namedb *db, const struct dname *domain_dname, + struct zone *zone, int *exact, struct domain **result) +{ + dname_type const *hash_dname; + struct nsec3_domain *nsec3_domain; + + *result = NULL; + *exact = 0; + hash_dname = nsec3_hash_dname(region, zone, domain_dname); + *exact = nsec3_find_cover(db, zone, hash_dname, &nsec3_domain); + if (nsec3_domain != NULL) { + *result = nsec3_domain->nsec3_domain; + } + return; +} + +static void +nsec3_hash_and_find_wild_cover(struct region *region, + struct namedb *db, struct domain *domain, + struct zone *zone, int *exact, struct domain **result) +{ + struct dname const *wcard_child; + /* find cover for *.domain for wildcard denial */ + (void) dname_make_wildcard(region, domain_dname(domain), + &wcard_child); + nsec3_hash_and_find_cover(region, db, wcard_child, zone, exact, + result); + if ((*exact != 0) && + (domain_wildcard_child(domain) == NULL)) { + /* We found an exact match for the *.domain NSEC3 hash, + * but the domain wildcard child (*.domain) does not exist. + * Thus there is a hash collision. It will cause servfail + * for NXdomain queries below this domain. + */ + log_msg(LOG_WARNING, + "collision of wildcard denial for %s. " + "Sign zone with different salt to remove collision.", + dname_to_string(domain_dname(domain), NULL)); + } +} +#endif + + /* add the NSEC3 rrset to the query answer at the given domain */ static void nsec3_add_rrset(struct query *query, struct answer *answer, @@ -421,16 +717,27 @@ static void nsec3_add_nonexist_proof(struct query *query, struct answer *answer, struct domain *encloser, struct namedb* db, const dname_type* qname) { - const dname_type *to_prove, *hashed; - domain_type *cover=0; + const dname_type *to_prove; +#ifdef FULL_PREHASH + const dname_type *hashed; +#else + int exact = 0; +#endif + domain_type *cover = NULL; assert(encloser); /* if query=a.b.c.d encloser=c.d. then proof needed for b.c.d. */ /* if query=a.b.c.d encloser=*.c.d. then proof needed for b.c.d. */ to_prove = dname_partial_copy(query->region, qname, dname_label_match_count(qname, domain_dname(encloser))+1); /* generate proof that one label below closest encloser does not exist */ +#ifdef FULL_PREHASH hashed = nsec3_hash_dname(query->region, query->zone, to_prove); if(nsec3_find_cover(db, query->zone, hashed, &cover)) +#else + nsec3_hash_and_find_cover(query->region, db, to_prove, query->zone, + &exact, &cover); + if (exact) +#endif { /* exact match, hash collision */ /* the hashed name of the query corresponds to an existing name. */ @@ -439,8 +746,7 @@ nsec3_add_nonexist_proof(struct query *query, struct answer *answer, RCODE_SET(query->packet, RCODE_SERVFAIL); return; } - else - { + else if (cover) { /* cover proves the qname does not exist */ nsec3_add_rrset(query, answer, AUTHORITY_SECTION, cover); } @@ -457,11 +763,16 @@ nsec3_add_closest_encloser_proof( /* prove that below closest encloser nothing exists */ nsec3_add_nonexist_proof(query, answer, closest_encloser, db, qname); /* proof that closest encloser exists */ +#ifdef FULL_PREHASH if(closest_encloser->nsec3_is_exact) +#else + if(closest_encloser->nsec3_cover) +#endif nsec3_add_rrset(query, answer, AUTHORITY_SECTION, closest_encloser->nsec3_cover); } + void nsec3_answer_wildcard(struct query *query, struct answer *answer, struct domain *wildcard, struct namedb* db, const dname_type* qname) @@ -473,25 +784,47 @@ nsec3_answer_wildcard(struct query *query, struct answer *answer, nsec3_add_nonexist_proof(query, answer, wildcard, db, qname); } + static void nsec3_add_ds_proof(struct query *query, struct answer *answer, struct domain *domain, int delegpt) { +#ifndef FULL_PREHASH + struct domain * ds_parent_cover = NULL; + int exact = 0; +#endif /* assert we are above the zone cut */ assert(domain != query->zone->apex); + +#ifdef FULL_PREHASH if(domain->nsec3_ds_parent_is_exact) { /* use NSEC3 record from above the zone cut. */ nsec3_add_rrset(query, answer, AUTHORITY_SECTION, domain->nsec3_ds_parent_cover); } else if (!delegpt && domain->nsec3_is_exact) { +#else + nsec3_hash_and_find_cover(query->region, NULL, domain_dname(domain), + query->zone, &exact, &ds_parent_cover); + if (exact) { + /* use NSEC3 record from above the zone cut. */ + if (ds_parent_cover) { + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + ds_parent_cover); + } + } else if (!delegpt && domain->nsec3_cover) { +#endif nsec3_add_rrset(query, answer, AUTHORITY_SECTION, domain->nsec3_cover); } else { /* prove closest provable encloser */ domain_type* par = domain->parent; - domain_type* prev_par = 0; + domain_type* prev_par = NULL; +#ifdef FULL_PREHASH while(par && !par->nsec3_is_exact) +#else + while(par && !par->nsec3_cover) +#endif { prev_par = par; par = par->parent; @@ -503,30 +836,54 @@ nsec3_add_ds_proof(struct query *query, struct answer *answer, the one below it has no exact nsec3, disprove it. disprove is easy, it has a prehashed cover ptr. */ if(prev_par) { +#ifdef FULL_PREHASH assert(prev_par != domain && !prev_par->nsec3_is_exact); nsec3_add_rrset(query, answer, AUTHORITY_SECTION, prev_par->nsec3_cover); +#else + struct domain *prev_parent_cover = NULL; + nsec3_hash_and_find_cover(query->region, NULL, + domain_dname(prev_par), query->zone, + &exact, &prev_parent_cover); + if (prev_parent_cover) { + nsec3_add_rrset(query, answer, + AUTHORITY_SECTION, prev_parent_cover); + } +#endif } + + /* use NSEC3 record from above the zone cut. */ /* add optout range from parent zone */ /* note: no check of optout bit, resolver checks it */ +#ifdef FULL_PREHASH nsec3_add_rrset(query, answer, AUTHORITY_SECTION, domain->nsec3_ds_parent_cover); +#else + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + ds_parent_cover); +#endif } } + void nsec3_answer_nodata(struct query *query, struct answer *answer, struct domain *original) { if(!query->zone->nsec3_soa_rr) return; + /* nodata when asking for secure delegation */ if(query->qtype == TYPE_DS) { if(original == query->zone->apex) { /* DS at zone apex, but server not authoritative for parent zone */ /* so answer at the child zone level */ +#ifdef FULL_PREHASH if(original->nsec3_is_exact) +#else + if(original->nsec3_cover) +#endif nsec3_add_rrset(query, answer, AUTHORITY_SECTION, original->nsec3_cover); return; @@ -537,17 +894,47 @@ nsec3_answer_nodata(struct query *query, struct answer *answer, /* the nodata is result from a wildcard match */ else if (original==original->wildcard_child_closest_match && label_is_wildcard(dname_name(domain_dname(original)))) { +#ifndef FULL_PREHASH + struct domain* original_cover; + int exact; +#endif /* denial for wildcard is already there */ + /* add parent proof to have a closest encloser proof for wildcard parent */ + /* in other words: nsec3 matching closest encloser */ +#ifdef FULL_PREHASH if(original->parent && original->parent->nsec3_is_exact) +#else + if(original->parent && original->parent->nsec3_cover) +#endif nsec3_add_rrset(query, answer, AUTHORITY_SECTION, original->parent->nsec3_cover); + /* proof for wildcard itself */ + /* in other words: nsec3 matching source of synthesis */ +#ifdef FULL_PREHASH nsec3_add_rrset(query, answer, AUTHORITY_SECTION, original->nsec3_cover); +#else + original_cover = original->nsec3_cover; + if (!original_cover) { /* not exact */ + nsec3_hash_and_find_cover(query->region, NULL, + domain_dname(original), query->zone, + &exact, &original_cover); + } + if (original_cover) { + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + original_cover); + } +#endif + } - else { /* add nsec3 to prove rrset does not exist */ + else { /* add nsec3 to prove rrset does not exist */ +#ifdef FULL_PREHASH if(original->nsec3_is_exact) +#else + if (original->nsec3_cover != NULL) +#endif nsec3_add_rrset(query, answer, AUTHORITY_SECTION, original->nsec3_cover); } @@ -588,11 +975,17 @@ nsec3_answer_authoritative(struct domain** match, struct query *query, struct answer *answer, struct domain* closest_encloser, struct namedb* db, const dname_type* qname) { +#ifndef FULL_PREHASH + struct domain *cover_domain = NULL; + int exact = 0; +#endif + if(!query->zone->nsec3_soa_rr) return; assert(match); /* there is a match, this has 1 RRset, which is NSEC3, but qtype is not. */ - if(*match && + /* !is_existing: no RR types exist at the QNAME, nor at any descendant of QNAME */ + if(*match && !(*match)->is_existing && #if 0 query->qtype != TYPE_NSEC3 && #endif @@ -601,14 +994,30 @@ nsec3_answer_authoritative(struct domain** match, struct query *query, /* act as if the NSEC3 domain did not exist, name error */ *match = 0; /* all nsec3s are directly below the apex, that is closest encloser */ +#ifdef FULL_PREHASH if(query->zone->apex->nsec3_is_exact) +#else + if(query->zone->apex->nsec3_cover) +#endif nsec3_add_rrset(query, answer, AUTHORITY_SECTION, query->zone->apex->nsec3_cover); + /* disprove the nsec3 record. */ - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, closest_encloser->nsec3_cover); + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + closest_encloser->nsec3_cover); /* disprove a wildcard */ - nsec3_add_rrset(query, answer, AUTHORITY_SECTION, query->zone->apex-> - nsec3_wcard_child_cover); +#ifdef FULL_PREHASH + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + query->zone->apex->nsec3_wcard_child_cover); +#else + cover_domain = NULL; + nsec3_hash_and_find_cover(query->region, db, + domain_dname(closest_encloser), + query->zone, &exact, &cover_domain); + if (cover_domain) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + cover_domain); +#endif if (domain_wildcard_child(query->zone->apex)) { /* wildcard exists below the domain */ /* wildcard and nsec3 domain clash. server failure. */ @@ -616,12 +1025,31 @@ nsec3_answer_authoritative(struct domain** match, struct query *query, } return; } + else if(*match && (*match)->is_existing && +#if 0 + query->qtype != TYPE_NSEC3 && +#endif + domain_has_only_NSEC3(*match, query->zone)) + { + /* this looks like a NSEC3 domain, but is actually an empty non-terminal. */ + nsec3_answer_nodata(query, answer, *match); + return; + } if(!*match) { /* name error, domain does not exist */ - nsec3_add_closest_encloser_proof(query, answer, closest_encloser, - db, qname); + nsec3_add_closest_encloser_proof(query, answer, + closest_encloser, db, qname); +#ifdef FULL_PREHASH nsec3_add_rrset(query, answer, AUTHORITY_SECTION, closest_encloser->nsec3_wcard_child_cover); +#else + cover_domain = NULL; + nsec3_hash_and_find_wild_cover(query->region, db, + closest_encloser, query->zone, &exact, &cover_domain); + if (cover_domain) + nsec3_add_rrset(query, answer, AUTHORITY_SECTION, + cover_domain); +#endif } } diff --git a/usr.sbin/nsd/query.c b/usr.sbin/nsd/query.c index 3f04597e763..738bae23bf7 100644 --- a/usr.sbin/nsd/query.c +++ b/usr.sbin/nsd/query.c @@ -773,7 +773,12 @@ answer_delegation(query_type *query, answer_type *answer) assert(query->delegation_domain); assert(query->delegation_rrset); - AA_CLR(query->packet); + if (query->cname_count == 0) { + AA_CLR(query->packet); + } else { + AA_SET(query->packet); + } + add_rrset(query, answer, AUTHORITY_SECTION, @@ -923,6 +928,9 @@ answer_domain(struct nsd* nsd, struct query *q, answer_type *answer, domain_dname(closest_match)); q->zone = origzone; } + /* example 6.2.7 shows no NS-set from zone in auth (RFC1034) */ + q->domain = domain; + return; } else { answer_nodata(q, answer, original); return; @@ -931,7 +939,7 @@ answer_domain(struct nsd* nsd, struct query *q, answer_type *answer, q->domain = domain; if (q->qclass != CLASS_ANY && q->zone->ns_rrset && answer_needs_ns(q)) { - add_rrset(q, answer, AUTHORITY_SECTION, q->zone->apex, + add_rrset(q, answer, OPTIONAL_AUTHORITY_SECTION, q->zone->apex, q->zone->ns_rrset); } } @@ -1032,12 +1040,13 @@ answer_authoritative(struct nsd *nsd, match->rrsets = wildcard_child->rrsets; match->is_existing = wildcard_child->is_existing; #ifdef NSEC3 - match->nsec3_is_exact = wildcard_child->nsec3_is_exact; match->nsec3_cover = wildcard_child->nsec3_cover; +#ifdef FULL_PREHASH + match->nsec3_is_exact = wildcard_child->nsec3_is_exact; match->nsec3_wcard_child_cover = wildcard_child->nsec3_wcard_child_cover; match->nsec3_ds_parent_is_exact = wildcard_child->nsec3_ds_parent_is_exact; match->nsec3_ds_parent_cover = wildcard_child->nsec3_ds_parent_cover; - +#endif if (q->edns.dnssec_ok && q->zone->nsec3_soa_rr) { /* Only add nsec3 wildcard data when do bit is set */ nsec3_answer_wildcard(q, answer, wildcard_child, nsd->db, qname); @@ -1199,9 +1208,14 @@ answer_query(struct nsd *nsd, struct query *q) answer_lookup_zone(nsd, q, &answer, 0, exact, closest_match, closest_encloser, q->qname); + encode_answer(q, &answer); + if (ANCOUNT(q->packet) + NSCOUNT(q->packet) + ARCOUNT(q->packet) == 0) + { + /* no answers, no need for compression */ + return; + } offset = dname_label_offsets(q->qname)[domain_dname(closest_encloser)->label_count - 1] + QHEADERSZ; query_add_compression_domain(q, closest_encloser, offset); - encode_answer(q, &answer); query_clear_compression_tables(q); } @@ -1368,6 +1382,8 @@ query_add_optional(query_type *q, nsd_type *nsd) case EDNS_NOT_PRESENT: break; case EDNS_OK: + if (q->edns.dnssec_ok) edns->ok[7] = 0x80; + else edns->ok[7] = 0x00; buffer_write(q->packet, edns->ok, OPT_LEN); if (nsd->nsid_len > 0 && q->edns.nsid == 1 && !query_overflow_nsid(q, nsd->nsid_len)) { @@ -1385,6 +1401,8 @@ query_add_optional(query_type *q, nsd_type *nsd) STATUP(nsd, edns); break; case EDNS_ERROR: + if (q->edns.dnssec_ok) edns->error[7] = 0x80; + else edns->error[7] = 0x00; buffer_write(q->packet, edns->error, OPT_LEN); buffer_write(q->packet, edns->rdata_none, OPT_RDATA); ARCOUNT_SET(q->packet, ARCOUNT(q->packet) + 1); diff --git a/usr.sbin/nsd/rdata.c b/usr.sbin/nsd/rdata.c index 20af8d852a6..c0f6a0b23e8 100644 --- a/usr.sbin/nsd/rdata.c +++ b/usr.sbin/nsd/rdata.c @@ -126,7 +126,7 @@ rdata_text_to_string(buffer_type *output, rdata_atom_type rdata, } buffer_printf(output, "%c", ch); } else { - buffer_printf(output, "\\%03u", (unsigned) ch); + buffer_printf(output, "\\%03u", (unsigned) data[i]); } } buffer_printf(output, "\""); @@ -152,7 +152,7 @@ rdata_texts_to_string(buffer_type *output, rdata_atom_type rdata, } buffer_printf(output, "%c", ch); } else { - buffer_printf(output, "\\%03u", (unsigned) ch); + buffer_printf(output, "\\%03u", (unsigned) data[pos+i]); } } pos += data[pos]+1; @@ -303,6 +303,8 @@ rdata_base64_to_string(buffer_type *output, rdata_atom_type rdata, { int length; size_t size = rdata_atom_size(rdata); + if(size == 0) + return 1; buffer_reserve(output, size * 2 + 1); length = b64_ntop(rdata_atom_data(rdata), size, (char *) buffer_current(output), size * 2); @@ -452,7 +454,17 @@ rdata_ipsecgateway_to_string(buffer_type *output, rdata_atom_type rdata, rr_type rdata_aaaa_to_string(output, rdata, rr); break; case IPSECKEY_DNAME: - rdata_dname_to_string(output, rdata, rr); + { + region_type* temp = region_create(xalloc, free); + const dname_type* d = dname_make(temp, + rdata_atom_data(rdata), 0); + if(!d) { + region_destroy(temp); + return 0; + } + buffer_printf(output, "%s", dname_to_string(d, NULL)); + region_destroy(temp); + } break; default: return 0; @@ -708,6 +720,9 @@ rdata_wireformat_to_rdata_atoms(region_type *region, break; } } + if (!required && buffer_position(packet) == end) { + break; + } temp_rdatas[i].data = (uint16_t *) region_alloc( region, sizeof(uint16_t) + length); diff --git a/usr.sbin/nsd/server.c b/usr.sbin/nsd/server.c index 28ba14353c4..ed81863ddb1 100644 --- a/usr.sbin/nsd/server.c +++ b/usr.sbin/nsd/server.c @@ -83,32 +83,32 @@ struct tcp_handler_data * data, including this structure. This region is destroyed * when the connection is closed. */ - region_type* region; + region_type *region; /* * The global nsd structure. */ - struct nsd* nsd; + struct nsd *nsd; /* * The current query data for this TCP connection. */ - query_type* query; + query_type *query; /* * These fields are used to enable the TCP accept handlers * when the number of TCP connection drops below the maximum * number of TCP connections. */ - size_t tcp_accept_handler_count; - netio_handler_type* tcp_accept_handlers; + size_t tcp_accept_handler_count; + netio_handler_type *tcp_accept_handlers; /* * The query_state is used to remember if we are performing an * AXFR, if we're done processing, or if we should discard the * query and connection. */ - query_state_type query_state; + query_state_type query_state; /* * The bytes_transmitted field is used to remember the number @@ -116,7 +116,7 @@ struct tcp_handler_data * packet. The count includes the two additional bytes used * to specify the packet length on a TCP connection. */ - size_t bytes_transmitted; + size_t bytes_transmitted; /* * The number of queries handled by this specific TCP connection. @@ -759,7 +759,9 @@ server_reload(struct nsd *nsd, region_type* server_region, netio_type* netio, region_log_stats(nsd->db->region); #endif /* NDEBUG */ #ifdef NSEC3 +#ifdef FULL_PREHASH prehash(nsd->db, 1); +#endif /* FULL_PREHASH */ #endif /* NSEC3 */ initialize_dname_compression_tables(nsd); @@ -1678,6 +1680,9 @@ handle_tcp_writing(netio_type *netio, #ifdef ECONNRESET if(verbosity >= 2 || errno != ECONNRESET) #endif /* ECONNRESET */ +#ifdef EPIPE + if(verbosity >= 2 || errno != EPIPE) +#endif /* EPIPE 'broken pipe' */ log_msg(LOG_ERR, "failed writing to tcp: %s", strerror(errno)); cleanup_tcp_handler(netio, handler); return; @@ -1712,6 +1717,9 @@ handle_tcp_writing(netio_type *netio, #ifdef ECONNRESET if(verbosity >= 2 || errno != ECONNRESET) #endif /* ECONNRESET */ +#ifdef EPIPE + if(verbosity >= 2 || errno != EPIPE) +#endif /* EPIPE 'broken pipe' */ log_msg(LOG_ERR, "failed writing to tcp: %s", strerror(errno)); cleanup_tcp_handler(netio, handler); return; diff --git a/usr.sbin/nsd/zonec.8.in b/usr.sbin/nsd/zonec.8.in index 7442b27b841..b394af02639 100644 --- a/usr.sbin/nsd/zonec.8.in +++ b/usr.sbin/nsd/zonec.8.in @@ -1,10 +1,10 @@ -.TH "nsd\-zonec" "8" "Mar 22, 2011" "NLnet Labs" "nsd 3.2.8" +.TH "nsd\-zonec" "8" "Nov 23, 2011" "NLnet Labs" "nsd 3.2.9" .\" Copyright (c) 2001\-2011, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" .LP .B nsd\-zonec -\- NSD zone compiler version 3.2.8. +\- NSD zone compiler version 3.2.9. .SH "SYNOPSIS" .LP .B nsd\-zonec diff --git a/usr.sbin/nsd/zonec.c b/usr.sbin/nsd/zonec.c index f7a63c26658..b719beb8057 100644 --- a/usr.sbin/nsd/zonec.c +++ b/usr.sbin/nsd/zonec.c @@ -1,7 +1,7 @@ /* * zonec.c -- zone compiler. * - * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. + * Copyright (c) 2001-2011, NLnet Labs. All rights reserved. * * See LICENSE for the license. * @@ -1162,7 +1162,10 @@ process_rr(void) /* We only support IN class */ if (rr->klass != CLASS_IN) { - zc_error_prev_line("only class IN is supported"); + if(zone->opts && zone->opts->request_xfr) + zc_warning_prev_line("only class IN is supported"); + else + zc_error_prev_line("only class IN is supported"); return 0; } @@ -1198,9 +1201,12 @@ process_rr(void) * This is a SOA record, start a new zone or continue * an existing one. */ - if (rr->owner->is_apex) - zc_error_prev_line("this SOA record was already encountered"); - else if (rr->owner == parser->default_apex) { + if (rr->owner->is_apex) { + if(zone->opts && zone->opts->request_xfr) + zc_warning_prev_line("this SOA record was already encountered"); + else + zc_error_prev_line("this SOA record was already encountered"); + } else if (rr->owner == parser->default_apex) { zone->apex = rr->owner; rr->owner->is_apex = 1; } @@ -1212,7 +1218,10 @@ process_rr(void) if (!dname_is_subdomain(domain_dname(rr->owner), domain_dname(zone->apex))) { - zc_error_prev_line("out of zone data"); + if(zone->opts && zone->opts->request_xfr) + zc_warning_prev_line("out of zone data"); + else + zc_error_prev_line("out of zone data"); return 0; } @@ -1257,21 +1266,33 @@ process_rr(void) } if(rr->type == TYPE_DNAME && rrset->rr_count > 1) { - zc_error_prev_line("multiple DNAMEs at the same name"); + if(zone->opts && zone->opts->request_xfr) + zc_warning_prev_line("multiple DNAMEs at the same name"); + else + zc_error_prev_line("multiple DNAMEs at the same name"); } if(rr->type == TYPE_CNAME && rrset->rr_count > 1) { - zc_error_prev_line("multiple CNAMEs at the same name"); + if(zone->opts && zone->opts->request_xfr) + zc_warning_prev_line("multiple CNAMEs at the same name"); + else + zc_error_prev_line("multiple CNAMEs at the same name"); } if((rr->type == TYPE_DNAME && domain_find_rrset(rr->owner, zone, TYPE_CNAME)) ||(rr->type == TYPE_CNAME && domain_find_rrset(rr->owner, zone, TYPE_DNAME))) { - zc_error_prev_line("DNAME and CNAME at the same name"); + if(zone->opts && zone->opts->request_xfr) + zc_warning_prev_line("DNAME and CNAME at the same name"); + else + zc_error_prev_line("DNAME and CNAME at the same name"); } if(domain_find_rrset(rr->owner, zone, TYPE_CNAME) && domain_find_non_cname_rrset(rr->owner, zone)) { - zc_error_prev_line("CNAME and other data at the same name"); + if(zone->opts && zone->opts->request_xfr) + zc_warning_prev_line("CNAME and other data at the same name"); + else + zc_error_prev_line("CNAME and other data at the same name"); } - if (rr->type == TYPE_RRSIG && rr_rrsig_type_covered(rr) == TYPE_SOA) { + if (rr->type == TYPE_RRSIG && rr_rrsig_type_covered(rr) == TYPE_DNSKEY) { rrset->zone->is_secure = 1; } @@ -1287,7 +1308,10 @@ process_rr(void) } } else if (rr->type == TYPE_SOA) { - zc_error_prev_line("duplicate SOA record discarded"); + if(zone->opts && zone->opts->request_xfr) + zc_warning_prev_line("duplicate SOA record discarded"); + else + zc_error_prev_line("duplicate SOA record discarded"); --rrset->rr_count; } |