summaryrefslogtreecommitdiffstats
path: root/usr.sbin/nsd
diff options
context:
space:
mode:
authorsthen <sthen@openbsd.org>2019-12-18 10:58:18 +0000
committersthen <sthen@openbsd.org>2019-12-18 10:58:18 +0000
commit5435475d426b8472bbbd30dcd1b34cc38879de70 (patch)
tree3021abd1c3fb08223d64a3dbeda80aa89a60ddad /usr.sbin/nsd
parentimport NSD 4.2.4, ok florian (diff)
downloadwireguard-openbsd-5435475d426b8472bbbd30dcd1b34cc38879de70.tar.xz
wireguard-openbsd-5435475d426b8472bbbd30dcd1b34cc38879de70.zip
merge NSD 4.2.4
Diffstat (limited to 'usr.sbin/nsd')
-rw-r--r--usr.sbin/nsd/Makefile.in8
-rw-r--r--usr.sbin/nsd/config.h.in9
-rw-r--r--usr.sbin/nsd/configlexer.lex21
-rw-r--r--usr.sbin/nsd/configparser.y1786
-rw-r--r--usr.sbin/nsd/configure32
-rw-r--r--usr.sbin/nsd/configure.ac9
-rw-r--r--usr.sbin/nsd/nsd-checkconf.8.in2
-rw-r--r--usr.sbin/nsd/nsd-checkconf.c3
-rw-r--r--usr.sbin/nsd/nsd-checkzone.8.in2
-rw-r--r--usr.sbin/nsd/nsd-control.8.in2
-rw-r--r--usr.sbin/nsd/nsd-control.c2
-rw-r--r--usr.sbin/nsd/nsd.8.in4
-rw-r--r--usr.sbin/nsd/nsd.c338
-rw-r--r--usr.sbin/nsd/nsd.conf.5.in31
-rw-r--r--usr.sbin/nsd/nsd.conf.sample.in23
-rw-r--r--usr.sbin/nsd/nsd.h20
-rw-r--r--usr.sbin/nsd/options.c183
-rw-r--r--usr.sbin/nsd/options.h25
-rw-r--r--usr.sbin/nsd/query.c11
-rw-r--r--usr.sbin/nsd/server.c1426
-rw-r--r--usr.sbin/nsd/tsig.c2
-rw-r--r--usr.sbin/nsd/xfrd.c21
22 files changed, 1872 insertions, 2088 deletions
diff --git a/usr.sbin/nsd/Makefile.in b/usr.sbin/nsd/Makefile.in
index 0fba1027b88..ae81833822d 100644
--- a/usr.sbin/nsd/Makefile.in
+++ b/usr.sbin/nsd/Makefile.in
@@ -178,12 +178,16 @@ xfr-inspect: xfr-inspect.o $(COMMON_OBJ) $(LIBOBJS)
clean:
rm -f *.o $(TARGETS) $(MANUALS) cutest udb-inspect xfr-inspect nsd-mem
-realclean: clean
- rm -f Makefile config.h config.log config.status
+distclean: clean
+ rm -f Makefile config.h config.log config.status dnstap/dnstap_config.h
+
+realclean: distclean
rm -rf autom4te*
rm -f zlexer.c zparser.h zparser.c zparser.stamp
rm -f configlexer.c configparser.h configparser.c configparser.stamp
+maintainer-clean: realclean
+
devclean: realclean
rm -f config.h.in configure
diff --git a/usr.sbin/nsd/config.h.in b/usr.sbin/nsd/config.h.in
index 5b141522d5a..617e7390516 100644
--- a/usr.sbin/nsd/config.h.in
+++ b/usr.sbin/nsd/config.h.in
@@ -73,6 +73,9 @@
/* Define to 1 if you have the `clock_gettime' function. */
#undef HAVE_CLOCK_GETTIME
+/* Define to 1 if you have the `CRYPTO_memcmp' function. */
+#undef HAVE_CRYPTO_MEMCMP
+
/* if time.h provides ctime_r prototype */
#undef HAVE_CTIME_R_PROTO
@@ -87,6 +90,9 @@
/* Define to 1 if you have the `dup2' function. */
#undef HAVE_DUP2
+/* Define to 1 if you have the `EC_KEY_new_by_curve_name' function. */
+#undef HAVE_EC_KEY_NEW_BY_CURVE_NAME
+
/* Define to 1 if you have the <endian.h> header file. */
#undef HAVE_ENDIAN_H
@@ -208,6 +214,9 @@
/* Define to 1 if you have the `mmap' function. */
#undef HAVE_MMAP
+/* If sys/socket.h has a struct mmsghdr. */
+#undef HAVE_MMSGHDR
+
/* Define to 1 if you have the `munmap' function. */
#undef HAVE_MUNMAP
diff --git a/usr.sbin/nsd/configlexer.lex b/usr.sbin/nsd/configlexer.lex
index 0ea97969139..cd92add3234 100644
--- a/usr.sbin/nsd/configlexer.lex
+++ b/usr.sbin/nsd/configlexer.lex
@@ -21,7 +21,6 @@
#include "options.h"
#include "configyyrename.h"
#include "configparser.h"
-void c_error(const char *message);
#if 0
#define LEXOUT(s) printf s /* used ONLY when debugging */
@@ -54,27 +53,27 @@ static void config_start_include(const char* filename)
struct inc_state* s;
char* nm;
if(inc_depth++ > 10000000) {
- c_error_msg("too many include files");
+ yyerror("too many include files");
return;
}
if(strlen(filename) == 0) {
- c_error_msg("empty include file name");
+ yyerror("empty include file name");
return;
}
s = (struct inc_state*)malloc(sizeof(*s));
if(!s) {
- c_error_msg("include %s: malloc failure", filename);
+ yyerror("include %s: malloc failure", filename);
return;
}
nm = strdup(filename);
if(!nm) {
- c_error_msg("include %s: strdup failure", filename);
+ yyerror("include %s: strdup failure", filename);
free(s);
return;
}
input = fopen(filename, "r");
if(!input) {
- c_error_msg("cannot open include file '%s': %s",
+ yyerror("cannot open include file '%s': %s",
filename, strerror(errno));
free(s);
free(nm);
@@ -97,13 +96,12 @@ static void config_start_include_glob(const char* filename)
/* check for wildcards */
#ifdef HAVE_GLOB
glob_t g;
- size_t i;
- int r, flags;
+ int i, r, flags;
#endif /* HAVE_GLOB */
if (cfg_parser->chroot) {
int l = strlen(cfg_parser->chroot); /* chroot has trailing slash */
if (strncmp(cfg_parser->chroot, filename, l) != 0) {
- c_error_msg("include file '%s' is not relative to chroot '%s'",
+ yyerror("include file '%s' is not relative to chroot '%s'",
filename, cfg_parser->chroot);
return;
}
@@ -137,7 +135,7 @@ static void config_start_include_glob(const char* filename)
return;
}
/* process files found, if any */
- for(i=0; i<(size_t)g.gl_pathc; i++) {
+ for(i=(int)g.gl_pathc-1; i>=0; i--) {
config_start_include(g.gl_pathv[i]);
}
globfree(&g);
@@ -253,7 +251,7 @@ key{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_KEY;}
algorithm{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ALGORITHM;}
secret{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SECRET;}
pattern{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_PATTERN;}
-include-pattern{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_INCLUDEPATTERN;}
+include-pattern{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_INCLUDE_PATTERN;}
remote-control{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_REMOTE_CONTROL;}
control-enable{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_ENABLE;}
control-interface{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CONTROL_INTERFACE;}
@@ -285,6 +283,7 @@ dnstap-log-auth-response-messages{COLON} { LEXOUT(("v(%s) ", yytext)); return VA
log-time-ascii{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_LOG_TIME_ASCII;}
round-robin{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ROUND_ROBIN;}
minimal-responses{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MINIMAL_RESPONSES;}
+confine-to-zone{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_CONFINE_TO_ZONE;}
refuse-any{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_REFUSE_ANY;}
max-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_REFRESH_TIME;}
min-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_REFRESH_TIME;}
diff --git a/usr.sbin/nsd/configparser.y b/usr.sbin/nsd/configparser.y
index f1520e0bf90..179bc7e9155 100644
--- a/usr.sbin/nsd/configparser.y
+++ b/usr.sbin/nsd/configparser.y
@@ -1,7 +1,7 @@
/*
* configparser.y -- yacc grammar for NSD configuration files
*
- * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
+ * Copyright (c) 2001-2019, NLnet Labs. All rights reserved.
*
* See LICENSE for the license.
*
@@ -10,11 +10,10 @@
%{
#include "config.h"
-#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
#include <stdio.h>
#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
#include "options.h"
#include "util.h"
@@ -22,1105 +21,758 @@
#include "tsig.h"
#include "rrl.h"
#include "configyyrename.h"
-int c_lex(void);
-void c_error(const char *message);
+
+int yylex(void);
#ifdef __cplusplus
extern "C"
-#endif /* __cplusplus */
+#endif
/* these need to be global, otherwise they cannot be used inside yacc */
-extern config_parser_state_type* cfg_parser;
-
-#if 0
-#define OUTYY(s) printf s /* used ONLY when debugging */
-#else
-#define OUTYY(s)
-#endif
+extern config_parser_state_type *cfg_parser;
+static void append_acl(struct acl_options **list, struct acl_options *acl);
%}
+
%union {
- char* str;
+ char *str;
+ long long llng;
+ int bln;
+ struct ip_address_option *ip;
}
-%token SPACE LETTER NEWLINE COMMENT COLON ANY ZONESTR
%token <str> STRING
-%token VAR_SERVER VAR_NAME VAR_IP_ADDRESS VAR_IP_TRANSPARENT VAR_DEBUG_MODE
-%token VAR_IP4_ONLY VAR_IP6_ONLY VAR_DATABASE VAR_IDENTITY VAR_NSID VAR_LOGFILE
-%token VAR_SERVER_COUNT VAR_TCP_COUNT VAR_PIDFILE VAR_PORT VAR_STATISTICS
-%token VAR_CHROOT VAR_USERNAME VAR_ZONESDIR VAR_XFRDFILE VAR_DIFFFILE
-%token VAR_XFRD_RELOAD_TIMEOUT VAR_TCP_QUERY_COUNT VAR_TCP_TIMEOUT
-%token VAR_IPV4_EDNS_SIZE VAR_IPV6_EDNS_SIZE VAR_DO_IP4 VAR_DO_IP6
-%token VAR_TCP_MSS VAR_OUTGOING_TCP_MSS VAR_IP_FREEBIND
-%token VAR_ZONEFILE
-%token VAR_ZONE
-%token VAR_ALLOW_NOTIFY VAR_REQUEST_XFR VAR_NOTIFY VAR_PROVIDE_XFR VAR_SIZE_LIMIT_XFR
-%token VAR_NOTIFY_RETRY VAR_OUTGOING_INTERFACE VAR_ALLOW_AXFR_FALLBACK
-%token VAR_KEY
-%token VAR_ALGORITHM VAR_SECRET
-%token VAR_AXFR VAR_UDP
-%token VAR_VERBOSITY VAR_HIDE_VERSION VAR_HIDE_IDENTITY
-%token VAR_PATTERN VAR_INCLUDEPATTERN VAR_ZONELISTFILE
-%token VAR_REMOTE_CONTROL VAR_CONTROL_ENABLE VAR_CONTROL_INTERFACE
-%token VAR_CONTROL_PORT VAR_SERVER_KEY_FILE VAR_SERVER_CERT_FILE
-%token VAR_CONTROL_KEY_FILE VAR_CONTROL_CERT_FILE VAR_XFRDIR
-%token VAR_RRL_SIZE VAR_RRL_RATELIMIT VAR_RRL_SLIP
-%token VAR_RRL_IPV4_PREFIX_LENGTH VAR_RRL_IPV6_PREFIX_LENGTH
-%token VAR_RRL_WHITELIST_RATELIMIT VAR_RRL_WHITELIST
-%token VAR_ZONEFILES_CHECK VAR_ZONEFILES_WRITE VAR_LOG_TIME_ASCII
-%token VAR_ROUND_ROBIN VAR_ZONESTATS VAR_REUSEPORT VAR_VERSION
-%token VAR_MAX_REFRESH_TIME VAR_MIN_REFRESH_TIME
-%token VAR_MAX_RETRY_TIME VAR_MIN_RETRY_TIME
-%token VAR_MULTI_MASTER_CHECK VAR_MINIMAL_RESPONSES VAR_REFUSE_ANY
-%token VAR_USE_SYSTEMD VAR_DNSTAP VAR_DNSTAP_ENABLE VAR_DNSTAP_SOCKET_PATH
-%token VAR_DNSTAP_SEND_IDENTITY VAR_DNSTAP_SEND_VERSION VAR_DNSTAP_IDENTITY
-%token VAR_DNSTAP_VERSION VAR_DNSTAP_LOG_AUTH_QUERY_MESSAGES
-%token VAR_DNSTAP_LOG_AUTH_RESPONSE_MESSAGES
-%token VAR_TLS_SERVICE_KEY VAR_TLS_SERVICE_OCSP VAR_TLS_SERVICE_PEM VAR_TLS_PORT
-%token VAR_SEND_BUFFER_SIZE VAR_RECEIVE_BUFFER_SIZE
+%type <llng> number
+%type <bln> boolean
+%type <ip> ip_address
+
+/* server */
+%token VAR_SERVER
+%token VAR_SERVER_COUNT
+%token VAR_IP_ADDRESS
+%token VAR_IP_TRANSPARENT
+%token VAR_IP_FREEBIND
+%token VAR_REUSEPORT
+%token VAR_SEND_BUFFER_SIZE
+%token VAR_RECEIVE_BUFFER_SIZE
+%token VAR_DEBUG_MODE
+%token VAR_IP4_ONLY
+%token VAR_IP6_ONLY
+%token VAR_DO_IP4
+%token VAR_DO_IP6
+%token VAR_PORT
+%token VAR_USE_SYSTEMD
+%token VAR_VERBOSITY
+%token VAR_USERNAME
+%token VAR_CHROOT
+%token VAR_ZONESDIR
+%token VAR_ZONELISTFILE
+%token VAR_DATABASE
+%token VAR_LOGFILE
+%token VAR_PIDFILE
+%token VAR_DIFFFILE
+%token VAR_XFRDFILE
+%token VAR_XFRDIR
+%token VAR_HIDE_VERSION
+%token VAR_HIDE_IDENTITY
+%token VAR_VERSION
+%token VAR_IDENTITY
+%token VAR_NSID
+%token VAR_TCP_COUNT
%token VAR_TCP_REJECT_OVERFLOW
+%token VAR_TCP_QUERY_COUNT
+%token VAR_TCP_TIMEOUT
+%token VAR_TCP_MSS
+%token VAR_OUTGOING_TCP_MSS
+%token VAR_IPV4_EDNS_SIZE
+%token VAR_IPV6_EDNS_SIZE
+%token VAR_STATISTICS
+%token VAR_XFRD_RELOAD_TIMEOUT
+%token VAR_LOG_TIME_ASCII
+%token VAR_ROUND_ROBIN
+%token VAR_MINIMAL_RESPONSES
+%token VAR_CONFINE_TO_ZONE
+%token VAR_REFUSE_ANY
+%token VAR_ZONEFILES_CHECK
+%token VAR_ZONEFILES_WRITE
+%token VAR_RRL_SIZE
+%token VAR_RRL_RATELIMIT
+%token VAR_RRL_SLIP
+%token VAR_RRL_IPV4_PREFIX_LENGTH
+%token VAR_RRL_IPV6_PREFIX_LENGTH
+%token VAR_RRL_WHITELIST_RATELIMIT
+%token VAR_TLS_SERVICE_KEY
+%token VAR_TLS_SERVICE_PEM
+%token VAR_TLS_SERVICE_OCSP
+%token VAR_TLS_PORT
+
+/* dnstap */
+%token VAR_DNSTAP
+%token VAR_DNSTAP_ENABLE
+%token VAR_DNSTAP_SOCKET_PATH
+%token VAR_DNSTAP_SEND_IDENTITY
+%token VAR_DNSTAP_SEND_VERSION
+%token VAR_DNSTAP_IDENTITY
+%token VAR_DNSTAP_VERSION
+%token VAR_DNSTAP_LOG_AUTH_QUERY_MESSAGES
+%token VAR_DNSTAP_LOG_AUTH_RESPONSE_MESSAGES
+
+/* remote-control */
+%token VAR_REMOTE_CONTROL
+%token VAR_CONTROL_ENABLE
+%token VAR_CONTROL_INTERFACE
+%token VAR_CONTROL_PORT
+%token VAR_SERVER_KEY_FILE
+%token VAR_SERVER_CERT_FILE
+%token VAR_CONTROL_KEY_FILE
+%token VAR_CONTROL_CERT_FILE
+
+/* key */
+%token VAR_KEY
+%token VAR_ALGORITHM
+%token VAR_SECRET
+
+/* pattern */
+%token VAR_PATTERN
+%token VAR_NAME
+%token VAR_ZONEFILE
+%token VAR_NOTIFY
+%token VAR_PROVIDE_XFR
+%token VAR_AXFR
+%token VAR_UDP
+%token VAR_NOTIFY_RETRY
+%token VAR_ALLOW_NOTIFY
+%token VAR_REQUEST_XFR
+%token VAR_ALLOW_AXFR_FALLBACK
+%token VAR_OUTGOING_INTERFACE
+%token VAR_MAX_REFRESH_TIME
+%token VAR_MIN_REFRESH_TIME
+%token VAR_MAX_RETRY_TIME
+%token VAR_MIN_RETRY_TIME
+%token VAR_MULTI_MASTER_CHECK
+%token VAR_SIZE_LIMIT_XFR
+%token VAR_ZONESTATS
+%token VAR_INCLUDE_PATTERN
+
+/* zone */
+%token VAR_ZONE
+%token VAR_RRL_WHITELIST
%%
-toplevelvars: /* empty */ | toplevelvars toplevelvar ;
-toplevelvar: serverstart contents_server | zonestart contents_zone |
- keystart contents_key | patternstart contents_pattern |
- rcstart contents_rc | dtstart contents_dt;
-/* server: declaration */
-serverstart: VAR_SERVER
- { OUTYY(("\nP(server:)\n"));
- if(cfg_parser->server_settings_seen) {
- yyerror("duplicate server: element.");
- }
- cfg_parser->server_settings_seen = 1;
- }
- ;
-contents_server: contents_server content_server | ;
-content_server: server_ip_address | server_ip_transparent | server_debug_mode | server_ip4_only |
- server_ip6_only | server_database | server_identity | server_nsid | server_logfile |
- server_server_count | server_tcp_count | server_pidfile | server_port |
- server_statistics | server_chroot | server_username | server_zonesdir |
- server_difffile | server_xfrdfile | server_xfrd_reload_timeout |
- server_tcp_query_count | server_tcp_timeout | server_ipv4_edns_size |
- server_ipv6_edns_size | server_verbosity | server_hide_version |
- server_zonelistfile | server_xfrdir |
- server_tcp_mss | server_outgoing_tcp_mss |
- server_rrl_size | server_rrl_ratelimit | server_rrl_slip |
- server_rrl_ipv4_prefix_length | server_rrl_ipv6_prefix_length | server_rrl_whitelist_ratelimit |
- server_zonefiles_check | server_do_ip4 | server_do_ip6 |
- server_zonefiles_write | server_log_time_ascii | server_round_robin |
- server_reuseport | server_version | server_ip_freebind |
- server_tls_service_key | server_tls_service_pem | server_tls_port |
- server_minimal_responses | server_refuse_any | server_use_systemd |
- server_hide_identity | server_tls_service_ocsp |
- server_send_buffer_size | server_receive_buffer_size |
- server_tcp_reject_overflow;
-server_ip_address: VAR_IP_ADDRESS STRING
- {
- OUTYY(("P(server_ip_address:%s)\n", $2));
- if(cfg_parser->current_ip_address_option) {
- cfg_parser->current_ip_address_option->next =
- (ip_address_option_type*)region_alloc(
- cfg_parser->opt->region, sizeof(ip_address_option_type));
- cfg_parser->current_ip_address_option =
- cfg_parser->current_ip_address_option->next;
- cfg_parser->current_ip_address_option->next=0;
- } else {
- cfg_parser->current_ip_address_option =
- (ip_address_option_type*)region_alloc(
- cfg_parser->opt->region, sizeof(ip_address_option_type));
- cfg_parser->current_ip_address_option->next=0;
- cfg_parser->opt->ip_addresses = cfg_parser->current_ip_address_option;
- }
+blocks:
+ /* may be empty */
+ | blocks block ;
- cfg_parser->current_ip_address_option->address =
- region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_ip_transparent: VAR_IP_TRANSPARENT STRING
- {
- OUTYY(("P(server_ip_transparent:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->ip_transparent = (strcmp($2, "yes")==0);
- }
- ;
-server_ip_freebind: VAR_IP_FREEBIND STRING
- {
- OUTYY(("P(server_ip_freebind:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->ip_freebind = (strcmp($2, "yes")==0);
- }
- ;
-server_send_buffer_size: VAR_SEND_BUFFER_SIZE STRING
- {
- int sz = atoi($2);
- OUTYY(("P(server_send_buffer_size:%s)\n", $2));
- if(sz < 0)
- yyerror("non-negative number expected");
- else cfg_parser->opt->send_buffer_size = sz;
- }
- ;
-server_receive_buffer_size: VAR_RECEIVE_BUFFER_SIZE STRING
- {
- int sz = atoi($2);
- OUTYY(("P(server_receive_buffer_size:%s)\n", $2));
- if(sz < 0)
- yyerror("non-negative number expected");
- else cfg_parser->opt->receive_buffer_size = sz;
- }
- ;
-server_debug_mode: VAR_DEBUG_MODE STRING
- {
- OUTYY(("P(server_debug_mode:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->debug_mode = (strcmp($2, "yes")==0);
- }
- ;
-server_use_systemd: VAR_USE_SYSTEMD STRING
- {
- OUTYY(("P(server_use_systemd:%s)\n", $2));
- }
- ;
-server_verbosity: VAR_VERBOSITY STRING
- {
- OUTYY(("P(server_verbosity:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- else cfg_parser->opt->verbosity = atoi($2);
- }
- ;
-server_hide_version: VAR_HIDE_VERSION STRING
- {
- OUTYY(("P(server_hide_version:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->hide_version = (strcmp($2, "yes")==0);
- }
- ;
-server_hide_identity: VAR_HIDE_IDENTITY STRING
- {
- OUTYY(("P(server_hide_identity:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->hide_identity = (strcmp($2, "yes")==0);
- }
- ;
-server_ip4_only: VAR_IP4_ONLY STRING
- {
- /* for backwards compatibility in config file with NSD3 */
- OUTYY(("P(server_ip4_only:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else if(strcmp($2, "yes")==0) {
- cfg_parser->opt->do_ip4 = 1;
- cfg_parser->opt->do_ip6 = 0;
- }
- }
- ;
-server_ip6_only: VAR_IP6_ONLY STRING
- {
- /* for backwards compatibility in config file with NSD3 */
- OUTYY(("P(server_ip6_only:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else if(strcmp($2, "yes")==0) {
- cfg_parser->opt->do_ip6 = 1;
- cfg_parser->opt->do_ip4 = 0;
- }
- }
- ;
-server_do_ip4: VAR_DO_IP4 STRING
- {
- OUTYY(("P(server_do_ip4:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->do_ip4 = (strcmp($2, "yes")==0);
- }
- ;
-server_do_ip6: VAR_DO_IP6 STRING
- {
- OUTYY(("P(server_do_ip6:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->do_ip6 = (strcmp($2, "yes")==0);
- }
- ;
-server_reuseport: VAR_REUSEPORT STRING
- {
- OUTYY(("P(server_reuseport:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->reuseport = (strcmp($2, "yes")==0);
- }
- ;
-server_database: VAR_DATABASE STRING
- {
- OUTYY(("P(server_database:%s)\n", $2));
- cfg_parser->opt->database = region_strdup(cfg_parser->opt->region, $2);
- if(cfg_parser->opt->database[0] == 0 &&
- cfg_parser->opt->zonefiles_write == 0)
- cfg_parser->opt->zonefiles_write = ZONEFILES_WRITE_INTERVAL;
- }
- ;
-server_identity: VAR_IDENTITY STRING
- {
- OUTYY(("P(server_identity:%s)\n", $2));
- cfg_parser->opt->identity = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_version: VAR_VERSION STRING
- {
- OUTYY(("P(server_version:%s)\n", $2));
- cfg_parser->opt->version = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_nsid: VAR_NSID STRING
- {
- unsigned char* nsid = 0;
- size_t nsid_len = 0;
+block:
+ server
+ | dnstap
+ | remote_control
+ | key
+ | pattern
+ | zone ;
- OUTYY(("P(server_nsid:%s)\n", $2));
+server:
+ VAR_SERVER server_block ;
- if (strncasecmp($2, "ascii_", 6) == 0) {
- nsid_len = strlen($2+6);
- if(nsid_len < 65535) {
- cfg_parser->opt->nsid = region_alloc(cfg_parser->opt->region, nsid_len*2+1);
- hex_ntop((uint8_t*)$2+6, nsid_len, (char*)cfg_parser->opt->nsid, nsid_len*2+1);
- } else
- yyerror("NSID too long");
- } else if (strlen($2) % 2 != 0) {
- yyerror("the NSID must be a hex string of an even length.");
- } else {
- nsid_len = strlen($2) / 2;
- if(nsid_len < 65535) {
- nsid = xalloc(nsid_len);
- if (hex_pton($2, nsid, nsid_len) == -1)
- yyerror("hex string cannot be parsed in NSID.");
- else
- cfg_parser->opt->nsid = region_strdup(cfg_parser->opt->region, $2);
- free(nsid);
- } else
- yyerror("NSID too long");
- }
- }
- ;
-server_logfile: VAR_LOGFILE STRING
- {
- OUTYY(("P(server_logfile:%s)\n", $2));
- cfg_parser->opt->logfile = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_log_time_ascii: VAR_LOG_TIME_ASCII STRING
- {
- OUTYY(("P(server_log_time_ascii:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else {
- cfg_parser->opt->log_time_ascii = (strcmp($2, "yes")==0);
- log_time_asc = cfg_parser->opt->log_time_ascii;
- }
- }
- ;
-server_round_robin: VAR_ROUND_ROBIN STRING
- {
- OUTYY(("P(server_round_robin:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else {
- cfg_parser->opt->round_robin = (strcmp($2, "yes")==0);
- round_robin = cfg_parser->opt->round_robin;
- }
- }
- ;
-server_minimal_responses: VAR_MINIMAL_RESPONSES STRING
- {
- OUTYY(("P(server_minimal_responses:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else {
- cfg_parser->opt->minimal_responses = (strcmp($2, "yes")==0);
- minimal_responses = cfg_parser->opt->minimal_responses;
- }
- }
- ;
-server_refuse_any: VAR_REFUSE_ANY STRING
- {
- OUTYY(("P(server_refuse_any:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else {
- cfg_parser->opt->refuse_any = (strcmp($2, "yes")==0);
- }
- }
- ;
-server_server_count: VAR_SERVER_COUNT STRING
- {
- OUTYY(("P(server_server_count:%s)\n", $2));
- if(atoi($2) <= 0)
- yyerror("number greater than zero expected");
- else cfg_parser->opt->server_count = atoi($2);
- }
- ;
-server_tcp_count: VAR_TCP_COUNT STRING
- {
- OUTYY(("P(server_tcp_count:%s)\n", $2));
- if(atoi($2) <= 0)
- yyerror("number greater than zero expected");
- else cfg_parser->opt->tcp_count = atoi($2);
- }
- ;
-server_tcp_reject_overflow: VAR_TCP_REJECT_OVERFLOW STRING
- {
- OUTYY(("P(server_reject_overflow_tcp:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) {
- yyerror("tcp-reject-overflow: expected yes or no.");
- } else {
- cfg_parser->opt->tcp_reject_overflow =
- (strcmp($2, "yes")==0);
- }
- }
- ;
-server_pidfile: VAR_PIDFILE STRING
- {
- OUTYY(("P(server_pidfile:%s)\n", $2));
- cfg_parser->opt->pidfile = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_port: VAR_PORT STRING
- {
- OUTYY(("P(server_port:%s)\n", $2));
- cfg_parser->opt->port = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_statistics: VAR_STATISTICS STRING
- {
- OUTYY(("P(server_statistics:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- else cfg_parser->opt->statistics = atoi($2);
- }
- ;
-server_chroot: VAR_CHROOT STRING
- {
- OUTYY(("P(server_chroot:%s)\n", $2));
- cfg_parser->opt->chroot = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_username: VAR_USERNAME STRING
- {
- OUTYY(("P(server_username:%s)\n", $2));
- cfg_parser->opt->username = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_zonesdir: VAR_ZONESDIR STRING
- {
- OUTYY(("P(server_zonesdir:%s)\n", $2));
- cfg_parser->opt->zonesdir = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_zonelistfile: VAR_ZONELISTFILE STRING
- {
- OUTYY(("P(server_zonelistfile:%s)\n", $2));
- cfg_parser->opt->zonelistfile = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_xfrdir: VAR_XFRDIR STRING
- {
- OUTYY(("P(server_xfrdir:%s)\n", $2));
- cfg_parser->opt->xfrdir = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_difffile: VAR_DIFFFILE STRING
- {
- OUTYY(("P(server_difffile:%s)\n", $2));
- /* ignore the value for backwards compatibility in config file*/
- }
- ;
-server_xfrdfile: VAR_XFRDFILE STRING
- {
- OUTYY(("P(server_xfrdfile:%s)\n", $2));
- cfg_parser->opt->xfrdfile = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_xfrd_reload_timeout: VAR_XFRD_RELOAD_TIMEOUT STRING
- {
- OUTYY(("P(server_xfrd_reload_timeout:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- cfg_parser->opt->xfrd_reload_timeout = atoi($2);
- }
- ;
-server_tcp_query_count: VAR_TCP_QUERY_COUNT STRING
- {
- OUTYY(("P(server_tcp_query_count:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- cfg_parser->opt->tcp_query_count = atoi($2);
- }
- ;
-server_tcp_timeout: VAR_TCP_TIMEOUT STRING
- {
- OUTYY(("P(server_tcp_timeout:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- cfg_parser->opt->tcp_timeout = atoi($2);
- }
- ;
-server_tcp_mss: VAR_TCP_MSS STRING
- {
- OUTYY(("P(server_tcp_mss:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- cfg_parser->opt->tcp_mss = atoi($2);
- }
- ;
-server_outgoing_tcp_mss: VAR_OUTGOING_TCP_MSS STRING
- {
- OUTYY(("P(server_outgoing_tcp_mss:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- cfg_parser->opt->outgoing_tcp_mss = atoi($2);
- }
- ;
-server_ipv4_edns_size: VAR_IPV4_EDNS_SIZE STRING
- {
- OUTYY(("P(server_ipv4_edns_size:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- cfg_parser->opt->ipv4_edns_size = atoi($2);
- }
- ;
-server_ipv6_edns_size: VAR_IPV6_EDNS_SIZE STRING
- {
- OUTYY(("P(server_ipv6_edns_size:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- cfg_parser->opt->ipv6_edns_size = atoi($2);
- }
- ;
-server_rrl_size: VAR_RRL_SIZE STRING
- {
- OUTYY(("P(server_rrl_size:%s)\n", $2));
+server_block:
+ server_block server_option | ;
+
+server_option:
+ VAR_IP_ADDRESS ip_address
+ {
+ struct ip_address_option *ip = cfg_parser->opt->ip_addresses;
+ if(ip == NULL) {
+ cfg_parser->opt->ip_addresses = $2;
+ } else {
+ while(ip->next) { ip = ip->next; }
+ ip->next = $2;
+ }
+ }
+ | VAR_SERVER_COUNT number
+ {
+ if ($2 > 0) {
+ cfg_parser->opt->server_count = (int)$2;
+ } else {
+ yyerror("expected a number greater than zero");
+ }
+ }
+ | VAR_IP_TRANSPARENT boolean
+ { cfg_parser->opt->ip_transparent = (int)$2; }
+ | VAR_IP_FREEBIND boolean
+ { cfg_parser->opt->ip_freebind = $2; }
+ | VAR_SEND_BUFFER_SIZE number
+ { cfg_parser->opt->send_buffer_size = (int)$2; }
+ | VAR_RECEIVE_BUFFER_SIZE number
+ { cfg_parser->opt->receive_buffer_size = (int)$2; }
+ | VAR_DEBUG_MODE boolean
+ { cfg_parser->opt->debug_mode = $2; }
+ | VAR_USE_SYSTEMD boolean
+ { /* ignored, deprecated */ }
+ | VAR_HIDE_VERSION boolean
+ { cfg_parser->opt->hide_version = $2; }
+ | VAR_HIDE_IDENTITY boolean
+ { cfg_parser->opt->hide_identity = $2; }
+ | VAR_IP4_ONLY boolean
+ { if($2) { cfg_parser->opt->do_ip4 = 1; cfg_parser->opt->do_ip6 = 0; } }
+ | VAR_IP6_ONLY boolean
+ { if($2) { cfg_parser->opt->do_ip4 = 0; cfg_parser->opt->do_ip6 = 1; } }
+ | VAR_DO_IP4 boolean
+ { cfg_parser->opt->do_ip4 = $2; }
+ | VAR_DO_IP6 boolean
+ { cfg_parser->opt->do_ip6 = $2; }
+ | VAR_DATABASE STRING
+ {
+ cfg_parser->opt->database = region_strdup(cfg_parser->opt->region, $2);
+ if(cfg_parser->opt->database[0] == 0 &&
+ cfg_parser->opt->zonefiles_write == 0)
+ {
+ cfg_parser->opt->zonefiles_write = ZONEFILES_WRITE_INTERVAL;
+ }
+ }
+ | VAR_IDENTITY STRING
+ { cfg_parser->opt->identity = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_VERSION STRING
+ { cfg_parser->opt->version = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_NSID STRING
+ {
+ unsigned char* nsid = 0;
+ size_t nsid_len = strlen($2);
+
+ if (strncasecmp($2, "ascii_", 6) == 0) {
+ nsid_len -= 6; /* discard "ascii_" */
+ if(nsid_len < 65535) {
+ cfg_parser->opt->nsid = region_alloc(cfg_parser->opt->region, nsid_len*2+1);
+ hex_ntop((uint8_t*)$2+6, nsid_len, (char*)cfg_parser->opt->nsid, nsid_len*2+1);
+ } else {
+ yyerror("NSID too long");
+ }
+ } else if (nsid_len % 2 != 0) {
+ yyerror("the NSID must be a hex string of an even length.");
+ } else {
+ nsid_len = nsid_len / 2;
+ if(nsid_len < 65535) {
+ nsid = xalloc(nsid_len);
+ if (hex_pton($2, nsid, nsid_len) == -1) {
+ yyerror("hex string cannot be parsed in NSID.");
+ } else {
+ cfg_parser->opt->nsid = region_strdup(cfg_parser->opt->region, $2);
+ }
+ free(nsid);
+ } else {
+ yyerror("NSID too long");
+ }
+ }
+ }
+ | VAR_LOGFILE STRING
+ { cfg_parser->opt->logfile = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_TCP_COUNT number
+ {
+ if ($2 > 0) {
+ cfg_parser->opt->tcp_count = (int)$2;
+ } else {
+ yyerror("expected a number greater than zero");
+ }
+ }
+ | VAR_TCP_REJECT_OVERFLOW boolean
+ { cfg_parser->opt->tcp_reject_overflow = $2; }
+ | VAR_TCP_QUERY_COUNT number
+ { cfg_parser->opt->tcp_query_count = (int)$2; }
+ | VAR_TCP_TIMEOUT number
+ { cfg_parser->opt->tcp_timeout = (int)$2; }
+ | VAR_TCP_MSS number
+ { cfg_parser->opt->tcp_mss = (int)$2; }
+ | VAR_OUTGOING_TCP_MSS number
+ { cfg_parser->opt->outgoing_tcp_mss = (int)$2; }
+ | VAR_IPV4_EDNS_SIZE number
+ { cfg_parser->opt->ipv4_edns_size = (size_t)$2; }
+ | VAR_IPV6_EDNS_SIZE number
+ { cfg_parser->opt->ipv6_edns_size = (size_t)$2; }
+ | VAR_PIDFILE STRING
+ { cfg_parser->opt->pidfile = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_PORT number
+ {
+ /* port number, stored as a string */
+ char buf[16];
+ (void)snprintf(buf, sizeof(buf), "%lld", $2);
+ cfg_parser->opt->port = region_strdup(cfg_parser->opt->region, buf);
+ }
+ | VAR_REUSEPORT boolean
+ { cfg_parser->opt->reuseport = $2; }
+ | VAR_STATISTICS number
+ { cfg_parser->opt->statistics = (int)$2; }
+ | VAR_CHROOT STRING
+ { cfg_parser->opt->chroot = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_USERNAME STRING
+ { cfg_parser->opt->username = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_ZONESDIR STRING
+ { cfg_parser->opt->zonesdir = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_ZONELISTFILE STRING
+ { cfg_parser->opt->zonelistfile = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_DIFFFILE STRING
+ { /* ignored, deprecated */ }
+ | VAR_XFRDFILE STRING
+ { cfg_parser->opt->xfrdfile = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_XFRDIR STRING
+ { cfg_parser->opt->xfrdir = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_XFRD_RELOAD_TIMEOUT number
+ { cfg_parser->opt->xfrd_reload_timeout = (int)$2; }
+ | VAR_VERBOSITY number
+ { cfg_parser->opt->verbosity = (int)$2; }
+ | VAR_RRL_SIZE number
+ {
#ifdef RATELIMIT
- if(atoi($2) <= 0)
- yyerror("number greater than zero expected");
- cfg_parser->opt->rrl_size = atoi($2);
+ if ($2 > 0) {
+ cfg_parser->opt->rrl_size = (size_t)$2;
+ } else {
+ yyerror("expected a number greater than zero");
+ }
#endif
- }
- ;
-server_rrl_ratelimit: VAR_RRL_RATELIMIT STRING
- {
- OUTYY(("P(server_rrl_ratelimit:%s)\n", $2));
+ }
+ | VAR_RRL_RATELIMIT number
+ {
#ifdef RATELIMIT
- cfg_parser->opt->rrl_ratelimit = atoi($2);
+ cfg_parser->opt->rrl_ratelimit = (size_t)$2;
#endif
- }
- ;
-server_rrl_slip: VAR_RRL_SLIP STRING
- {
- OUTYY(("P(server_rrl_slip:%s)\n", $2));
+ }
+ | VAR_RRL_SLIP number
+ {
#ifdef RATELIMIT
- if(atoi($2) < 0)
- yyerror("number equal or greater than zero expected");
- cfg_parser->opt->rrl_slip = atoi($2);
+ cfg_parser->opt->rrl_slip = (size_t)$2;
#endif
- }
- ;
-server_rrl_ipv4_prefix_length: VAR_RRL_IPV4_PREFIX_LENGTH STRING
- {
- OUTYY(("P(server_rrl_ipv4_prefix_length:%s)\n", $2));
+ }
+ | VAR_RRL_IPV4_PREFIX_LENGTH number
+ {
#ifdef RATELIMIT
- if(atoi($2) < 0 || atoi($2) > 32)
- yyerror("invalid IPv4 prefix length");
- cfg_parser->opt->rrl_ipv4_prefix_length = atoi($2);
+ if ($2 > 32) {
+ yyerror("invalid IPv4 prefix length");
+ } else {
+ cfg_parser->opt->rrl_ipv4_prefix_length = (size_t)$2;
+ }
#endif
- }
- ;
-server_rrl_ipv6_prefix_length: VAR_RRL_IPV6_PREFIX_LENGTH STRING
- {
- OUTYY(("P(server_rrl_ipv6_prefix_length:%s)\n", $2));
+ }
+ | VAR_RRL_IPV6_PREFIX_LENGTH number
+ {
#ifdef RATELIMIT
- if(atoi($2) < 0 || atoi($2) > 64)
- yyerror("invalid IPv6 prefix length");
- cfg_parser->opt->rrl_ipv6_prefix_length = atoi($2);
+ if ($2 > 64) {
+ yyerror("invalid IPv6 prefix length");
+ } else {
+ cfg_parser->opt->rrl_ipv6_prefix_length = (size_t)$2;
+ }
#endif
- }
- ;
-server_rrl_whitelist_ratelimit: VAR_RRL_WHITELIST_RATELIMIT STRING
- {
- OUTYY(("P(server_rrl_whitelist_ratelimit:%s)\n", $2));
+ }
+ | VAR_RRL_WHITELIST_RATELIMIT number
+ {
#ifdef RATELIMIT
- cfg_parser->opt->rrl_whitelist_ratelimit = atoi($2);
+ cfg_parser->opt->rrl_whitelist_ratelimit = (size_t)$2;
#endif
- }
- ;
-server_zonefiles_check: VAR_ZONEFILES_CHECK STRING
- {
- OUTYY(("P(server_zonefiles_check:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->zonefiles_check = (strcmp($2, "yes")==0);
- }
- ;
-server_zonefiles_write: VAR_ZONEFILES_WRITE STRING
- {
- OUTYY(("P(server_zonefiles_write:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- else cfg_parser->opt->zonefiles_write = atoi($2);
- }
- ;
-server_tls_service_key: VAR_TLS_SERVICE_KEY STRING
- {
- OUTYY(("P(server_tls_service_key:%s)\n", $2));
- cfg_parser->opt->tls_service_key = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_tls_service_ocsp: VAR_TLS_SERVICE_OCSP STRING
- {
- OUTYY(("P(server_tls_service_ocsp:%s)\n", $2));
- cfg_parser->opt->tls_service_ocsp = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_tls_service_pem: VAR_TLS_SERVICE_PEM STRING
- {
- OUTYY(("P(server_tls_service_pem:%s)\n", $2));
- cfg_parser->opt->tls_service_pem = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-server_tls_port: VAR_TLS_PORT STRING
- {
- OUTYY(("P(server_tls_port:%s)\n", $2));
- cfg_parser->opt->tls_port = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-rcstart: VAR_REMOTE_CONTROL
- {
- OUTYY(("\nP(remote-control:)\n"));
- }
- ;
-contents_rc: contents_rc content_rc
- | ;
-content_rc: rc_control_enable | rc_control_interface | rc_control_port |
- rc_server_key_file | rc_server_cert_file | rc_control_key_file |
- rc_control_cert_file
- ;
-rc_control_enable: VAR_CONTROL_ENABLE STRING
- {
- OUTYY(("P(control_enable:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->control_enable = (strcmp($2, "yes")==0);
- }
- ;
-rc_control_port: VAR_CONTROL_PORT STRING
- {
- OUTYY(("P(control_port:%s)\n", $2));
- if(atoi($2) == 0)
- yyerror("control port number expected");
- else cfg_parser->opt->control_port = atoi($2);
- }
- ;
-rc_control_interface: VAR_CONTROL_INTERFACE STRING
- {
- ip_address_option_type* last = NULL;
- ip_address_option_type* o = (ip_address_option_type*)region_alloc(
- cfg_parser->opt->region, sizeof(ip_address_option_type));
- OUTYY(("P(control_interface:%s)\n", $2));
- /* append at end */
- last = cfg_parser->opt->control_interface;
- while(last && last->next)
- last = last->next;
- if(last == NULL)
- cfg_parser->opt->control_interface = o;
- else last->next = o;
- o->next = NULL;
- o->address = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-rc_server_key_file: VAR_SERVER_KEY_FILE STRING
- {
- OUTYY(("P(rc_server_key_file:%s)\n", $2));
- cfg_parser->opt->server_key_file = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-rc_server_cert_file: VAR_SERVER_CERT_FILE STRING
- {
- OUTYY(("P(rc_server_cert_file:%s)\n", $2));
- cfg_parser->opt->server_cert_file = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-rc_control_key_file: VAR_CONTROL_KEY_FILE STRING
- {
- OUTYY(("P(rc_control_key_file:%s)\n", $2));
- cfg_parser->opt->control_key_file = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-rc_control_cert_file: VAR_CONTROL_CERT_FILE STRING
- {
- OUTYY(("P(rc_control_cert_file:%s)\n", $2));
- cfg_parser->opt->control_cert_file = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
+ }
+ | VAR_ZONEFILES_CHECK boolean
+ { cfg_parser->opt->zonefiles_check = $2; }
+ | VAR_ZONEFILES_WRITE number
+ { cfg_parser->opt->zonefiles_write = (int)$2; }
+ | VAR_LOG_TIME_ASCII boolean
+ {
+ cfg_parser->opt->log_time_ascii = $2;
+ log_time_asc = cfg_parser->opt->log_time_ascii;
+ }
+ | VAR_ROUND_ROBIN boolean
+ {
+ cfg_parser->opt->round_robin = $2;
+ round_robin = cfg_parser->opt->round_robin;
+ }
+ | VAR_MINIMAL_RESPONSES boolean
+ {
+ cfg_parser->opt->minimal_responses = $2;
+ minimal_responses = cfg_parser->opt->minimal_responses;
+ }
+ | VAR_CONFINE_TO_ZONE boolean
+ { cfg_parser->opt->confine_to_zone = $2; }
+ | VAR_REFUSE_ANY boolean
+ { cfg_parser->opt->refuse_any = $2; }
+ | VAR_TLS_SERVICE_KEY STRING
+ { cfg_parser->opt->tls_service_key = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_TLS_SERVICE_OCSP STRING
+ { cfg_parser->opt->tls_service_ocsp = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_TLS_SERVICE_PEM STRING
+ { cfg_parser->opt->tls_service_pem = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_TLS_PORT number
+ {
+ /* port number, stored as string */
+ char buf[16];
+ (void)snprintf(buf, sizeof(buf), "%lld", $2);
+ cfg_parser->opt->tls_port = region_strdup(cfg_parser->opt->region, buf);
+ }
+ ;
-/* dnstap: declaration */
-dtstart: VAR_DNSTAP
- {
- OUTYY(("\nP(dnstap:)\n"));
- }
- ;
-contents_dt: contents_dt content_dt
- | ;
-content_dt: dt_dnstap_enable | dt_dnstap_socket_path |
- dt_dnstap_send_identity | dt_dnstap_send_version |
- dt_dnstap_identity | dt_dnstap_version |
- dt_dnstap_log_auth_query_messages |
- dt_dnstap_log_auth_response_messages
- ;
-dt_dnstap_enable: VAR_DNSTAP_ENABLE STRING
- {
- OUTYY(("P(dt_dnstap_enable:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->dnstap_enable = (strcmp($2, "yes")==0);
- }
- ;
-dt_dnstap_socket_path: VAR_DNSTAP_SOCKET_PATH STRING
- {
- OUTYY(("P(dt_dnstap_socket_path:%s)\n", $2));
- cfg_parser->opt->dnstap_socket_path = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-dt_dnstap_send_identity: VAR_DNSTAP_SEND_IDENTITY STRING
- {
- OUTYY(("P(dt_dnstap_send_identity:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->dnstap_send_identity = (strcmp($2, "yes")==0);
- }
- ;
-dt_dnstap_send_version: VAR_DNSTAP_SEND_VERSION STRING
- {
- OUTYY(("P(dt_dnstap_send_version:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->dnstap_send_version = (strcmp($2, "yes")==0);
- }
- ;
-dt_dnstap_identity: VAR_DNSTAP_IDENTITY STRING
- {
- OUTYY(("P(dt_dnstap_identity:%s)\n", $2));
- cfg_parser->opt->dnstap_identity = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-dt_dnstap_version: VAR_DNSTAP_VERSION STRING
- {
- OUTYY(("P(dt_dnstap_version:%s)\n", $2));
- cfg_parser->opt->dnstap_version = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-dt_dnstap_log_auth_query_messages: VAR_DNSTAP_LOG_AUTH_QUERY_MESSAGES STRING
- {
- OUTYY(("P(dt_dnstap_log_auth_query_messages:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->dnstap_log_auth_query_messages = (strcmp($2, "yes")==0);
- }
- ;
-dt_dnstap_log_auth_response_messages: VAR_DNSTAP_LOG_AUTH_RESPONSE_MESSAGES STRING
- {
- OUTYY(("P(dt_dnstap_log_auth_response_messages:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->opt->dnstap_log_auth_response_messages = (strcmp($2, "yes")==0);
- }
- ;
+dnstap:
+ VAR_DNSTAP dnstap_block ;
-/* pattern: declaration */
-patternstart: VAR_PATTERN
- {
- OUTYY(("\nP(pattern:)\n"));
- if(cfg_parser->current_zone) {
- if(!cfg_parser->current_zone->name)
- c_error("previous zone has no name");
- else {
- if(!nsd_options_insert_zone(cfg_parser->opt,
- cfg_parser->current_zone))
- c_error("duplicate zone");
- }
- if(!cfg_parser->current_zone->pattern)
- c_error("previous zone has no pattern");
- cfg_parser->current_zone = NULL;
- }
- if(cfg_parser->current_pattern) {
- if(!cfg_parser->current_pattern->pname)
- c_error("previous pattern has no name");
- else {
- if(!nsd_options_insert_pattern(cfg_parser->opt,
- cfg_parser->current_pattern))
- c_error_msg("duplicate pattern %s",
- cfg_parser->current_pattern->pname);
- }
- }
- cfg_parser->current_pattern = pattern_options_create(
- cfg_parser->opt->region);
- cfg_parser->current_allow_notify = 0;
- cfg_parser->current_request_xfr = 0;
- cfg_parser->current_notify = 0;
- cfg_parser->current_provide_xfr = 0;
- cfg_parser->current_outgoing_interface = 0;
- }
- ;
-contents_pattern: contents_pattern content_pattern | content_pattern;
-content_pattern: pattern_name | zone_config_item;
-zone_config_item: zone_zonefile | zone_allow_notify | zone_request_xfr |
- zone_notify | zone_notify_retry | zone_provide_xfr |
- zone_outgoing_interface | zone_allow_axfr_fallback | include_pattern |
- zone_rrl_whitelist | zone_zonestats | zone_max_refresh_time |
- zone_min_refresh_time | zone_max_retry_time | zone_min_retry_time |
- zone_size_limit_xfr | zone_multi_master_check;
-pattern_name: VAR_NAME STRING
- {
- OUTYY(("P(pattern_name:%s)\n", $2));
-#ifndef NDEBUG
- assert(cfg_parser->current_pattern);
-#endif
- if(strchr($2, ' '))
- c_error_msg("space is not allowed in pattern name: "
- "'%s'", $2);
- cfg_parser->current_pattern->pname = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-include_pattern: VAR_INCLUDEPATTERN STRING
- {
- OUTYY(("P(include-pattern:%s)\n", $2));
-#ifndef NDEBUG
- assert(cfg_parser->current_pattern);
-#endif
- config_apply_pattern($2);
- }
- ;
+dnstap_block:
+ dnstap_block dnstap_option | ;
-/* zone: declaration */
-zonestart: VAR_ZONE
- {
- OUTYY(("\nP(zone:)\n"));
- if(cfg_parser->current_zone) {
- if(!cfg_parser->current_zone->name)
- c_error("previous zone has no name");
- else {
- if(!nsd_options_insert_zone(cfg_parser->opt,
- cfg_parser->current_zone))
- c_error("duplicate zone");
- }
- if(!cfg_parser->current_zone->pattern)
- c_error("previous zone has no pattern");
- }
- if(cfg_parser->current_pattern) {
- if(!cfg_parser->current_pattern->pname)
- c_error("previous pattern has no name");
- else {
- if(!nsd_options_insert_pattern(cfg_parser->opt,
- cfg_parser->current_pattern))
- c_error_msg("duplicate pattern %s",
- cfg_parser->current_pattern->pname);
- }
- }
- cfg_parser->current_zone = zone_options_create(cfg_parser->opt->region);
- cfg_parser->current_zone->part_of_config = 1;
- cfg_parser->current_pattern = pattern_options_create(
- cfg_parser->opt->region);
- cfg_parser->current_pattern->implicit = 1;
- cfg_parser->current_zone->pattern = cfg_parser->current_pattern;
- cfg_parser->current_allow_notify = 0;
- cfg_parser->current_request_xfr = 0;
- cfg_parser->current_notify = 0;
- cfg_parser->current_provide_xfr = 0;
- cfg_parser->current_outgoing_interface = 0;
- }
- ;
-contents_zone: contents_zone content_zone | content_zone;
-content_zone: zone_name | zone_config_item;
-zone_name: VAR_NAME STRING
- {
- char* s;
- OUTYY(("P(zone_name:%s)\n", $2));
-#ifndef NDEBUG
- assert(cfg_parser->current_zone);
- assert(cfg_parser->current_pattern);
-#endif
- cfg_parser->current_zone->name = region_strdup(cfg_parser->opt->region, $2);
- s = (char*)region_alloc(cfg_parser->opt->region,
- strlen($2)+strlen(PATTERN_IMPLICIT_MARKER)+1);
- memmove(s, PATTERN_IMPLICIT_MARKER,
- strlen(PATTERN_IMPLICIT_MARKER));
- memmove(s+strlen(PATTERN_IMPLICIT_MARKER), $2, strlen($2)+1);
- if(pattern_options_find(cfg_parser->opt, s))
- c_error_msg("zone %s cannot be created because "
- "implicit pattern %s already exists", $2, s);
- cfg_parser->current_pattern->pname = s;
- }
- ;
-zone_zonefile: VAR_ZONEFILE STRING
- {
- OUTYY(("P(zonefile:%s)\n", $2));
-#ifndef NDEBUG
- assert(cfg_parser->current_pattern);
-#endif
- cfg_parser->current_pattern->zonefile = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-zone_zonestats: VAR_ZONESTATS STRING
- {
- OUTYY(("P(zonestats:%s)\n", $2));
-#ifndef NDEBUG
- assert(cfg_parser->current_pattern);
-#endif
- cfg_parser->current_pattern->zonestats = region_strdup(cfg_parser->opt->region, $2);
- }
- ;
-zone_allow_notify: VAR_ALLOW_NOTIFY STRING STRING
- {
- acl_options_type* acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
- OUTYY(("P(allow_notify:%s %s)\n", $2, $3));
- if(cfg_parser->current_allow_notify)
- cfg_parser->current_allow_notify->next = acl;
- else
- cfg_parser->current_pattern->allow_notify = acl;
- cfg_parser->current_allow_notify = acl;
- }
- ;
-zone_request_xfr: VAR_REQUEST_XFR zone_request_xfr_data
- {
- }
- ;
-zone_size_limit_xfr: VAR_SIZE_LIMIT_XFR STRING
- {
- OUTYY(("P(size_limit_xfr:%s)\n", $2));
- if(atoll($2) < 0)
- yyerror("number >= 0 expected");
- else cfg_parser->current_pattern->size_limit_xfr = atoll($2);
- }
- ;
-zone_request_xfr_data: STRING STRING
- {
- acl_options_type* acl = parse_acl_info(cfg_parser->opt->region, $1, $2);
- OUTYY(("P(request_xfr:%s %s)\n", $1, $2));
- if(acl->blocked) c_error("blocked address used for request-xfr");
- if(acl->rangetype!=acl_range_single) c_error("address range used for request-xfr");
- if(cfg_parser->current_request_xfr)
- cfg_parser->current_request_xfr->next = acl;
- else
- cfg_parser->current_pattern->request_xfr = acl;
- cfg_parser->current_request_xfr = acl;
- }
- | VAR_AXFR STRING STRING
- {
- acl_options_type* acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
- acl->use_axfr_only = 1;
- OUTYY(("P(request_xfr:%s %s)\n", $2, $3));
- if(acl->blocked) c_error("blocked address used for request-xfr");
- if(acl->rangetype!=acl_range_single) c_error("address range used for request-xfr");
- if(cfg_parser->current_request_xfr)
- cfg_parser->current_request_xfr->next = acl;
- else
- cfg_parser->current_pattern->request_xfr = acl;
- cfg_parser->current_request_xfr = acl;
- }
- | VAR_UDP STRING STRING
- {
- acl_options_type* acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
- acl->allow_udp = 1;
- OUTYY(("P(request_xfr:%s %s)\n", $2, $3));
- if(acl->blocked) c_error("blocked address used for request-xfr");
- if(acl->rangetype!=acl_range_single) c_error("address range used for request-xfr");
- if(cfg_parser->current_request_xfr)
- cfg_parser->current_request_xfr->next = acl;
- else
- cfg_parser->current_pattern->request_xfr = acl;
- cfg_parser->current_request_xfr = acl;
- }
- ;
-zone_notify: VAR_NOTIFY STRING STRING
- {
- acl_options_type* acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
- OUTYY(("P(notify:%s %s)\n", $2, $3));
- if(acl->blocked) c_error("blocked address used for notify");
- if(acl->rangetype!=acl_range_single) c_error("address range used for notify");
- if(cfg_parser->current_notify)
- cfg_parser->current_notify->next = acl;
- else
- cfg_parser->current_pattern->notify = acl;
- cfg_parser->current_notify = acl;
- }
- ;
-zone_notify_retry: VAR_NOTIFY_RETRY STRING
- {
- OUTYY(("P(notify_retry:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- else {
- cfg_parser->current_pattern->notify_retry = atoi($2);
- cfg_parser->current_pattern->notify_retry_is_default=0;
- }
- }
- ;
-zone_provide_xfr: VAR_PROVIDE_XFR STRING STRING
- {
- acl_options_type* acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
- OUTYY(("P(provide_xfr:%s %s)\n", $2, $3));
- if(cfg_parser->current_provide_xfr)
- cfg_parser->current_provide_xfr->next = acl;
- else
- cfg_parser->current_pattern->provide_xfr = acl;
- cfg_parser->current_provide_xfr = acl;
- }
- ;
-zone_outgoing_interface: VAR_OUTGOING_INTERFACE STRING
- {
- acl_options_type* acl = parse_acl_info(cfg_parser->opt->region, $2, "NOKEY");
- OUTYY(("P(outgoing_interface:%s)\n", $2));
- if(acl->rangetype!=acl_range_single) c_error("address range used for outgoing interface");
- if(cfg_parser->current_outgoing_interface)
- cfg_parser->current_outgoing_interface->next = acl;
- else
- cfg_parser->current_pattern->outgoing_interface = acl;
- cfg_parser->current_outgoing_interface = acl;
- }
- ;
-zone_allow_axfr_fallback: VAR_ALLOW_AXFR_FALLBACK STRING
- {
- OUTYY(("P(allow_axfr_fallback:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else {
- cfg_parser->current_pattern->allow_axfr_fallback = (strcmp($2, "yes")==0);
- cfg_parser->current_pattern->allow_axfr_fallback_is_default = 0;
- }
- }
- ;
-zone_rrl_whitelist: VAR_RRL_WHITELIST STRING
- {
- OUTYY(("P(zone_rrl_whitelist:%s)\n", $2));
+dnstap_option:
+ VAR_DNSTAP_ENABLE boolean
+ { cfg_parser->opt->dnstap_enable = $2; }
+ | VAR_DNSTAP_SOCKET_PATH STRING
+ { cfg_parser->opt->dnstap_socket_path = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_DNSTAP_SEND_IDENTITY boolean
+ { cfg_parser->opt->dnstap_send_identity = $2; }
+ | VAR_DNSTAP_SEND_VERSION boolean
+ { cfg_parser->opt->dnstap_send_version = $2; }
+ | VAR_DNSTAP_IDENTITY STRING
+ { cfg_parser->opt->dnstap_identity = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_DNSTAP_VERSION STRING
+ { cfg_parser->opt->dnstap_version = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_DNSTAP_LOG_AUTH_QUERY_MESSAGES boolean
+ { cfg_parser->opt->dnstap_log_auth_query_messages = $2; }
+ | VAR_DNSTAP_LOG_AUTH_RESPONSE_MESSAGES boolean
+ { cfg_parser->opt->dnstap_log_auth_response_messages = $2; }
+ ;
+
+remote_control:
+ VAR_REMOTE_CONTROL remote_control_block ;
+
+remote_control_block:
+ remote_control_block remote_control_option | ;
+
+remote_control_option:
+ VAR_CONTROL_ENABLE boolean
+ { cfg_parser->opt->control_enable = $2; }
+ | VAR_CONTROL_INTERFACE ip_address
+ {
+ struct ip_address_option *ip = cfg_parser->opt->control_interface;
+ if(ip == NULL) {
+ cfg_parser->opt->control_interface = $2;
+ } else {
+ while(ip->next != NULL) { ip = ip->next; }
+ ip->next = $2;
+ }
+ }
+ | VAR_CONTROL_PORT number
+ {
+ if($2 == 0) {
+ yyerror("control port number expected");
+ } else {
+ cfg_parser->opt->control_port = (int)$2;
+ }
+ }
+ | VAR_SERVER_KEY_FILE STRING
+ { cfg_parser->opt->server_key_file = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_SERVER_CERT_FILE STRING
+ { cfg_parser->opt->server_cert_file = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_CONTROL_KEY_FILE STRING
+ { cfg_parser->opt->control_key_file = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_CONTROL_CERT_FILE STRING
+ { cfg_parser->opt->control_cert_file = region_strdup(cfg_parser->opt->region, $2); }
+ ;
+
+key:
+ VAR_KEY
+ {
+ key_options_type *key = key_options_create(cfg_parser->opt->region);
+ key->algorithm = region_strdup(cfg_parser->opt->region, "sha256");
+ assert(cfg_parser->key == NULL);
+ cfg_parser->key = key;
+ }
+ key_block
+ {
+ struct key_options *key = cfg_parser->key;
+ if(key->name == NULL) {
+ yyerror("tsig key has no name");
+ } else if(key->algorithm == NULL) {
+ yyerror("tsig key %s has no algorithm", key->name);
+ } else if(key->secret == NULL) {
+ yyerror("tsig key %s has no secret blob", key->name);
+ } else if(key_options_find(cfg_parser->opt, key->name)) {
+ yyerror("duplicate tsig key %s", key->name);
+ } else {
+ key_options_insert(cfg_parser->opt, key);
+ cfg_parser->key = NULL;
+ }
+ } ;
+
+key_block:
+ key_block key_option | ;
+
+key_option:
+ VAR_NAME STRING
+ {
+ dname_type *dname;
+
+ dname = (dname_type *)dname_parse(cfg_parser->opt->region, $2);
+ cfg_parser->key->name = region_strdup(cfg_parser->opt->region, $2);
+ if(dname == NULL) {
+ yyerror("bad tsig key name %s", $2);
+ } else {
+ region_recycle(cfg_parser->opt->region, dname, dname_total_size(dname));
+ }
+ }
+ | VAR_ALGORITHM STRING
+ {
+ if(tsig_get_algorithm_by_name($2) == NULL) {
+ yyerror("bad tsig key algorithm %s", $2);
+ } else {
+ cfg_parser->key->algorithm = region_strdup(cfg_parser->opt->region, $2);
+ }
+ }
+ | VAR_SECRET STRING
+ {
+ uint8_t data[16384];
+ int size;
+
+ cfg_parser->key->secret = region_strdup(cfg_parser->opt->region, $2);
+ size = __b64_pton($2, data, sizeof(data));
+ if(size == -1) {
+ yyerror("cannot base64 decode tsig secret %s",
+ cfg_parser->key->name?
+ cfg_parser->key->name:"");
+ } else if(size != 0) {
+ memset(data, 0xdd, size); /* wipe secret */
+ }
+ } ;
+
+
+zone:
+ VAR_ZONE
+ {
+ assert(cfg_parser->pattern == NULL);
+ assert(cfg_parser->zone == NULL);
+ cfg_parser->zone = zone_options_create(cfg_parser->opt->region);
+ cfg_parser->zone->part_of_config = 1;
+ cfg_parser->zone->pattern = cfg_parser->pattern =
+ pattern_options_create(cfg_parser->opt->region);
+ cfg_parser->zone->pattern->implicit = 1;
+ }
+ zone_block
+ {
+ assert(cfg_parser->zone != NULL);
+ if(cfg_parser->zone->name == NULL) {
+ yyerror("zone has no name");
+ } else if(!nsd_options_insert_zone(cfg_parser->opt, cfg_parser->zone)) {
+ yyerror("duplicate zone %s", cfg_parser->zone->name);
+ } else if(!nsd_options_insert_pattern(cfg_parser->opt, cfg_parser->zone->pattern)) {
+ yyerror("duplicate pattern %s", cfg_parser->zone->pattern->pname);
+ }
+ cfg_parser->pattern = NULL;
+ cfg_parser->zone = NULL;
+ } ;
+
+zone_block:
+ zone_block zone_option | ;
+
+zone_option:
+ VAR_NAME STRING
+ {
+ const char *marker = PATTERN_IMPLICIT_MARKER;
+ char *pname = region_alloc(cfg_parser->opt->region, strlen($2) + strlen(marker) + 1);
+ memmove(pname, marker, strlen(marker));
+ memmove(pname + strlen(marker), $2, strlen($2) + 1);
+ cfg_parser->zone->pattern->pname = pname;
+ cfg_parser->zone->name = region_strdup(cfg_parser->opt->region, $2);
+ if(pattern_options_find(cfg_parser->opt, pname)) {
+ yyerror("zone %s cannot be created because implicit pattern %s "
+ "already exists", $2, pname);
+ }
+ }
+ | pattern_or_zone_option ;
+
+pattern:
+ VAR_PATTERN
+ {
+ assert(cfg_parser->pattern == NULL);
+ cfg_parser->pattern = pattern_options_create(cfg_parser->opt->region);
+ }
+ pattern_block
+ {
+ pattern_options_type *pattern = cfg_parser->pattern;
+ if(pattern->pname == NULL) {
+ yyerror("pattern has no name");
+ } else if(!nsd_options_insert_pattern(cfg_parser->opt, pattern)) {
+ yyerror("duplicate pattern %s", pattern->pname);
+ }
+ cfg_parser->pattern = NULL;
+ } ;
+
+pattern_block:
+ pattern_block pattern_option | ;
+
+pattern_option:
+ VAR_NAME STRING
+ {
+ if(strchr($2, ' ')) {
+ yyerror("space is not allowed in pattern name: '%s'", $2);
+ }
+ cfg_parser->pattern->pname = region_strdup(cfg_parser->opt->region, $2);
+ }
+ | pattern_or_zone_option ;
+
+pattern_or_zone_option:
+ VAR_RRL_WHITELIST STRING
+ {
#ifdef RATELIMIT
- cfg_parser->current_pattern->rrl_whitelist |= rrlstr2type($2);
+ cfg_parser->pattern->rrl_whitelist |= rrlstr2type($2);
#endif
- }
- ;
-zone_max_refresh_time: VAR_MAX_REFRESH_TIME STRING
-{
- OUTYY(("P(zone_max_refresh_time:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- else {
- cfg_parser->current_pattern->max_refresh_time = atoi($2);
- cfg_parser->current_pattern->max_refresh_time_is_default = 0;
- }
-};
-zone_min_refresh_time: VAR_MIN_REFRESH_TIME STRING
-{
- OUTYY(("P(zone_min_refresh_time:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- else {
- cfg_parser->current_pattern->min_refresh_time = atoi($2);
- cfg_parser->current_pattern->min_refresh_time_is_default = 0;
- }
-};
-zone_max_retry_time: VAR_MAX_RETRY_TIME STRING
-{
- OUTYY(("P(zone_max_retry_time:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- else {
- cfg_parser->current_pattern->max_retry_time = atoi($2);
- cfg_parser->current_pattern->max_retry_time_is_default = 0;
- }
-};
-zone_min_retry_time: VAR_MIN_RETRY_TIME STRING
-{
- OUTYY(("P(zone_min_retry_time:%s)\n", $2));
- if(atoi($2) == 0 && strcmp($2, "0") != 0)
- yyerror("number expected");
- else {
- cfg_parser->current_pattern->min_retry_time = atoi($2);
- cfg_parser->current_pattern->min_retry_time_is_default = 0;
- }
-};
-zone_multi_master_check: VAR_MULTI_MASTER_CHECK STRING
- {
- OUTYY(("P(zone_multi_master_check:%s)\n", $2));
- if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
- yyerror("expected yes or no.");
- else cfg_parser->current_pattern->multi_master_check = (strcmp($2, "yes")==0);
- }
+ }
+ | VAR_ZONEFILE STRING
+ { cfg_parser->pattern->zonefile = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_ZONESTATS STRING
+ { cfg_parser->pattern->zonestats = region_strdup(cfg_parser->opt->region, $2); }
+ | VAR_SIZE_LIMIT_XFR number
+ {
+ if($2 > 0) {
+ cfg_parser->pattern->size_limit_xfr = (int)$2;
+ } else {
+ yyerror("expected a number greater than zero");
+ }
+ }
+ | VAR_MULTI_MASTER_CHECK boolean
+ { cfg_parser->pattern->multi_master_check = (int)$2; }
+ | VAR_INCLUDE_PATTERN STRING
+ { config_apply_pattern(cfg_parser->pattern, $2); }
+ | VAR_REQUEST_XFR STRING STRING
+ {
+ acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
+ if(acl->blocked)
+ yyerror("blocked address used for request-xfr");
+ if(acl->rangetype != acl_range_single)
+ yyerror("address range used for request-xfr");
+ append_acl(&cfg_parser->pattern->request_xfr, acl);
+ }
+ | VAR_REQUEST_XFR VAR_AXFR STRING STRING
+ {
+ acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $3, $4);
+ acl->use_axfr_only = 1;
+ if(acl->blocked)
+ yyerror("blocked address used for request-xfr");
+ if(acl->rangetype != acl_range_single)
+ yyerror("address range used for request-xfr");
+ append_acl(&cfg_parser->pattern->request_xfr, acl);
+ }
+ | VAR_REQUEST_XFR VAR_UDP STRING STRING
+ {
+ acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $3, $4);
+ acl->allow_udp = 1;
+ if(acl->blocked)
+ yyerror("blocked address used for request-xfr");
+ if(acl->rangetype != acl_range_single)
+ yyerror("address range used for request-xfr");
+ append_acl(&cfg_parser->pattern->request_xfr, acl);
+ }
+ | VAR_ALLOW_NOTIFY STRING STRING
+ {
+ acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
+ append_acl(&cfg_parser->pattern->allow_notify, acl);
+ }
+ | VAR_NOTIFY STRING STRING
+ {
+ acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
+ if(acl->blocked)
+ yyerror("blocked address used for notify");
+ if(acl->rangetype != acl_range_single)
+ yyerror("address range used for notify");
+ append_acl(&cfg_parser->pattern->notify, acl);
+ }
+ | VAR_PROVIDE_XFR STRING STRING
+ {
+ acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, $3);
+ append_acl(&cfg_parser->pattern->provide_xfr, acl);
+ }
+ | VAR_OUTGOING_INTERFACE STRING
+ {
+ acl_options_type *acl = parse_acl_info(cfg_parser->opt->region, $2, "NOKEY");
+ append_acl(&cfg_parser->pattern->outgoing_interface, acl);
+ }
+ | VAR_ALLOW_AXFR_FALLBACK boolean
+ {
+ cfg_parser->pattern->allow_axfr_fallback = $2;
+ cfg_parser->pattern->allow_axfr_fallback_is_default = 0;
+ }
+ | VAR_NOTIFY_RETRY number
+ {
+ cfg_parser->pattern->notify_retry = $2;
+ cfg_parser->pattern->notify_retry_is_default = 0;
+ }
+ | VAR_MAX_REFRESH_TIME number
+ {
+ cfg_parser->pattern->max_refresh_time = $2;
+ cfg_parser->pattern->max_refresh_time_is_default = 0;
+ }
+ | VAR_MIN_REFRESH_TIME number
+ {
+ cfg_parser->pattern->min_refresh_time = $2;
+ cfg_parser->pattern->min_refresh_time_is_default = 0;
+ }
+ | VAR_MAX_RETRY_TIME number
+ {
+ cfg_parser->pattern->max_retry_time = $2;
+ cfg_parser->pattern->max_retry_time_is_default = 0;
+ }
+ | VAR_MIN_RETRY_TIME number
+ {
+ cfg_parser->pattern->min_retry_time = $2;
+ cfg_parser->pattern->min_retry_time_is_default = 0;
+ } ;
-/* key: declaration */
-keystart: VAR_KEY
- {
- OUTYY(("\nP(key:)\n"));
- if(cfg_parser->current_key) {
- if(!cfg_parser->current_key->name) c_error("previous key has no name");
- if(!cfg_parser->current_key->algorithm) c_error("previous key has no algorithm");
- if(!cfg_parser->current_key->secret) c_error("previous key has no secret blob");
- key_options_insert(cfg_parser->opt, cfg_parser->current_key);
- }
- cfg_parser->current_key = key_options_create(cfg_parser->opt->region);
- cfg_parser->current_key->algorithm = region_strdup(cfg_parser->opt->region, "sha256");
- }
- ;
-contents_key: contents_key content_key | content_key;
-content_key: key_name | key_algorithm | key_secret;
-key_name: VAR_NAME STRING
- {
- const dname_type* d;
- OUTYY(("P(key_name:%s)\n", $2));
-#ifndef NDEBUG
- assert(cfg_parser->current_key);
-#endif
- cfg_parser->current_key->name = region_strdup(cfg_parser->opt->region, $2);
- d = dname_parse(cfg_parser->opt->region, $2);
- if(!d) c_error_msg("Failed to parse tsig key name %s", $2);
- else region_recycle(cfg_parser->opt->region, (void*)d,
- dname_total_size(d));
- }
- ;
-key_algorithm: VAR_ALGORITHM STRING
- {
- OUTYY(("P(key_algorithm:%s)\n", $2));
-#ifndef NDEBUG
- assert(cfg_parser->current_key);
-#endif
- if(cfg_parser->current_key->algorithm)
- region_recycle(cfg_parser->opt->region, cfg_parser->current_key->algorithm, strlen(cfg_parser->current_key->algorithm)+1);
- cfg_parser->current_key->algorithm = region_strdup(cfg_parser->opt->region, $2);
- if(tsig_get_algorithm_by_name($2) == NULL)
- c_error_msg("Bad tsig algorithm %s", $2);
- }
- ;
-key_secret: VAR_SECRET STRING
- {
- uint8_t data[16384];
- int size;
- OUTYY(("key_secret:%s)\n", $2));
-#ifndef NDEBUG
- assert(cfg_parser->current_key);
-#endif
- cfg_parser->current_key->secret = region_strdup(cfg_parser->opt->region, $2);
- size = __b64_pton($2, data, sizeof(data));
- if(size == -1) {
- c_error_msg("Cannot base64 decode tsig secret %s",
- cfg_parser->current_key->name?
- cfg_parser->current_key->name:"");
- } else if(size != 0) {
- memset(data, 0xdd, size); /* wipe secret */
- }
- }
- ;
+ip_address:
+ STRING
+ {
+ struct ip_address_option *ip = region_alloc_zero(
+ cfg_parser->opt->region, sizeof(*ip));
+ ip->address = region_strdup(cfg_parser->opt->region, $1);
+ $$ = ip;
+ } ;
+
+number:
+ STRING
+ {
+ /* ensure string consists entirely of digits */
+ const char *str = $1;
+ size_t pos = 0;
+ while(str[pos] >= '0' && str[pos] <= '9') {
+ pos++;
+ }
+
+ $$ = 0;
+ if(pos > 0 && str[pos] == '\0') {
+ int err = errno;
+ errno = 0;
+ $$ = strtoll(str, NULL, 10);
+ errno = err;
+ } else {
+ yyerror("expected a number");
+ YYABORT; /* trigger a parser error */
+ }
+ } ;
+
+boolean:
+ STRING
+ {
+ $$ = 0;
+ if(strcmp($1, "yes") == 0) {
+ $$ = 1;
+ } else if(strcmp($1, "no") == 0) {
+ $$ = 0;
+ } else {
+ yyerror("expected yes or no");
+ YYABORT; /* trigger a parser error */
+ }
+ } ;
%%
-/* parse helper routines could be here */
+static void
+append_acl(struct acl_options **list, struct acl_options *acl)
+{
+ assert(list != NULL);
+
+ if(*list == NULL) {
+ *list = acl;
+ } else {
+ struct acl_options *tail = *list;
+ while(tail->next != NULL)
+ tail = tail->next;
+ tail->next = acl;
+ }
+}
+
diff --git a/usr.sbin/nsd/configure b/usr.sbin/nsd/configure
index c4c9a63811b..f42b7ee7af0 100644
--- a/usr.sbin/nsd/configure
+++ b/usr.sbin/nsd/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for NSD 4.2.2.
+# Generated by GNU Autoconf 2.69 for NSD 4.2.4.
#
# Report bugs to <nsd-bugs@nlnetlabs.nl>.
#
@@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='NSD'
PACKAGE_TARNAME='nsd'
-PACKAGE_VERSION='4.2.2'
-PACKAGE_STRING='NSD 4.2.2'
+PACKAGE_VERSION='4.2.4'
+PACKAGE_STRING='NSD 4.2.4'
PACKAGE_BUGREPORT='nsd-bugs@nlnetlabs.nl'
PACKAGE_URL=''
@@ -1297,7 +1297,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures NSD 4.2.2 to adapt to many kinds of systems.
+\`configure' configures NSD 4.2.4 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1358,7 +1358,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of NSD 4.2.2:";;
+ short | recursive ) echo "Configuration of NSD 4.2.4:";;
esac
cat <<\_ACEOF
@@ -1514,7 +1514,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-NSD configure 4.2.2
+NSD configure 4.2.4
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2223,7 +2223,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by NSD $as_me 4.2.2, which was
+It was created by NSD $as_me 4.2.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -8107,6 +8107,18 @@ fi
done
+ac_fn_c_check_type "$LINENO" "struct mmsghdr" "ac_cv_type_struct_mmsghdr" "
+$ac_includes_default
+#include <sys/socket.h>
+
+"
+if test "x$ac_cv_type_struct_mmsghdr" = xyes; then :
+
+$as_echo "#define HAVE_MMSGHDR 1" >>confdefs.h
+
+fi
+
+
# Check whether --enable-recvmmsg was given.
if test "${enable_recvmmsg+set}" = set; then :
enableval=$enable_recvmmsg;
@@ -9149,7 +9161,7 @@ fi
done
- for ac_func in HMAC_CTX_reset HMAC_CTX_new EVP_cleanup ERR_load_crypto_strings OPENSSL_init_crypto SSL_CTX_set_security_level
+ for ac_func in HMAC_CTX_reset HMAC_CTX_new EVP_cleanup ERR_load_crypto_strings OPENSSL_init_crypto SSL_CTX_set_security_level CRYPTO_memcmp EC_KEY_new_by_curve_name
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -10291,7 +10303,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by NSD $as_me 4.2.2, which was
+This file was extended by NSD $as_me 4.2.4, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -10353,7 +10365,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-NSD config.status 4.2.2
+NSD config.status 4.2.4
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/usr.sbin/nsd/configure.ac b/usr.sbin/nsd/configure.ac
index e49fb3c3e9d..75907b70aca 100644
--- a/usr.sbin/nsd/configure.ac
+++ b/usr.sbin/nsd/configure.ac
@@ -5,7 +5,7 @@ dnl
sinclude(acx_nlnetlabs.m4)
sinclude(dnstap/dnstap.m4)
-AC_INIT(NSD,4.2.2,nsd-bugs@nlnetlabs.nl)
+AC_INIT(NSD,4.2.4,nsd-bugs@nlnetlabs.nl)
AC_CONFIG_HEADER([config.h])
CFLAGS="$CFLAGS"
@@ -630,6 +630,11 @@ AC_CHECK_FUNCS([arc4random arc4random_uniform])
AC_SEARCH_LIBS([setusercontext],[util],[AC_CHECK_HEADERS([login_cap.h])])
AC_CHECK_FUNCS([tzset alarm chroot dup2 endpwent gethostname memset memcpy pwrite socket strcasecmp strchr strdup strerror strncasecmp strtol writev getaddrinfo getnameinfo freeaddrinfo gai_strerror sigaction sigprocmask strptime strftime localtime_r setusercontext glob initgroups setresuid setreuid setresgid setregid getpwnam mmap ppoll clock_gettime accept4])
+AC_CHECK_TYPE([struct mmsghdr], AC_DEFINE(HAVE_MMSGHDR, 1, [If sys/socket.h has a struct mmsghdr.]), [], [
+AC_INCLUDES_DEFAULT
+#include <sys/socket.h>
+])
+
AC_ARG_ENABLE(recvmmsg, AC_HELP_STRING([--enable-recvmmsg], [Enable recvmmsg and sendmmsg compilation, faster but some kernel versions may have implementation problems for IPv6]))
case "$enable_recvmmsg" in
yes)
@@ -912,7 +917,7 @@ if test x$HAVE_SSL = x"yes"; then
SSL_LIBS="-lssl"
AC_SUBST(SSL_LIBS)
AC_CHECK_HEADERS([openssl/ssl.h openssl/err.h openssl/rand.h openssl/ocsp.h],,, [AC_INCLUDES_DEFAULT])
- AC_CHECK_FUNCS([HMAC_CTX_reset HMAC_CTX_new EVP_cleanup ERR_load_crypto_strings OPENSSL_init_crypto SSL_CTX_set_security_level])
+ AC_CHECK_FUNCS([HMAC_CTX_reset HMAC_CTX_new EVP_cleanup ERR_load_crypto_strings OPENSSL_init_crypto SSL_CTX_set_security_level CRYPTO_memcmp EC_KEY_new_by_curve_name])
AC_CHECK_DECLS([SSL_CTX_set_ecdh_auto,SSL_CTX_set_tmp_ecdh], [], [], [
AC_INCLUDES_DEFAULT
#ifdef HAVE_OPENSSL_ERR_H
diff --git a/usr.sbin/nsd/nsd-checkconf.8.in b/usr.sbin/nsd/nsd-checkconf.8.in
index 9afe139d3c8..748cc25dec5 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" "Aug 19, 2019" "NLnet Labs" "nsd 4.2.2"
+.TH "nsd\-checkconf" "8" "Dec 10, 2019" "NLnet Labs" "nsd 4.2.4"
.\" Copyright (c) 2001\-2008, 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 424796dac99..731a74fab40 100644
--- a/usr.sbin/nsd/nsd-checkconf.c
+++ b/usr.sbin/nsd/nsd-checkconf.c
@@ -371,6 +371,7 @@ config_print_zone(nsd_options_type* opt, const char* k, int s, const char *o,
SERV_GET_BIN(log_time_ascii, o);
SERV_GET_BIN(round_robin, o);
SERV_GET_BIN(minimal_responses, o);
+ SERV_GET_BIN(confine_to_zone, o);
SERV_GET_BIN(refuse_any, o);
SERV_GET_BIN(tcp_reject_overflow, o);
/* str */
@@ -533,6 +534,8 @@ config_test_print_server(nsd_options_type* opt)
printf("\tlog-time-ascii: %s\n", opt->log_time_ascii?"yes":"no");
printf("\tround-robin: %s\n", opt->round_robin?"yes":"no");
printf("\tminimal-responses: %s\n", opt->minimal_responses?"yes":"no");
+ printf("\tconfine-to-zone: %s\n",
+ opt->confine_to_zone ? "yes" : "no");
printf("\trefuse-any: %s\n", opt->refuse_any?"yes":"no");
printf("\tverbosity: %d\n", opt->verbosity);
for(ip = opt->ip_addresses; ip; ip=ip->next)
diff --git a/usr.sbin/nsd/nsd-checkzone.8.in b/usr.sbin/nsd/nsd-checkzone.8.in
index 7ade47b2af5..227da662c07 100644
--- a/usr.sbin/nsd/nsd-checkzone.8.in
+++ b/usr.sbin/nsd/nsd-checkzone.8.in
@@ -1,4 +1,4 @@
-.TH "nsd\-checkzone" "8" "Aug 19, 2019" "NLnet Labs" "nsd 4.2.2"
+.TH "nsd\-checkzone" "8" "Dec 10, 2019" "NLnet Labs" "nsd 4.2.4"
.\" Copyright (c) 2014, NLnet Labs. All rights reserved.
.\" See LICENSE for the license.
.SH "NAME"
diff --git a/usr.sbin/nsd/nsd-control.8.in b/usr.sbin/nsd/nsd-control.8.in
index 19e37355fc1..20e27a4fb09 100644
--- a/usr.sbin/nsd/nsd-control.8.in
+++ b/usr.sbin/nsd/nsd-control.8.in
@@ -1,4 +1,4 @@
-.TH "nsd\-control" "8" "Aug 19, 2019" "NLnet Labs" "nsd 4.2.2"
+.TH "nsd\-control" "8" "Dec 10, 2019" "NLnet Labs" "nsd 4.2.4"
.\" Copyright (c) 2011, NLnet Labs. All rights reserved.
.\" See LICENSE for the license.
.SH "NAME"
diff --git a/usr.sbin/nsd/nsd-control.c b/usr.sbin/nsd/nsd-control.c
index d6c7744d85e..649320eefa1 100644
--- a/usr.sbin/nsd/nsd-control.c
+++ b/usr.sbin/nsd/nsd-control.c
@@ -235,6 +235,7 @@ contact_server(const char* svr, struct nsd_options* cfg, int statuscmd)
addrfamily = AF_LOCAL;
port = 0;
#endif
+#ifdef INET6
} else if(strchr(svr, ':')) {
struct sockaddr_in6 sa;
addrlen = (socklen_t)sizeof(struct sockaddr_in6);
@@ -247,6 +248,7 @@ contact_server(const char* svr, struct nsd_options* cfg, int statuscmd)
}
memcpy(&addr, &sa, addrlen);
addrfamily = AF_INET6;
+#endif
} else { /* ip4 */
struct sockaddr_in sa;
addrlen = (socklen_t)sizeof(struct sockaddr_in);
diff --git a/usr.sbin/nsd/nsd.8.in b/usr.sbin/nsd/nsd.8.in
index ca9ae0218a2..cdba544f297 100644
--- a/usr.sbin/nsd/nsd.8.in
+++ b/usr.sbin/nsd/nsd.8.in
@@ -1,9 +1,9 @@
-.TH "NSD" "8" "Aug 19, 2019" "NLnet Labs" "NSD 4.2.2"
+.TH "NSD" "8" "Dec 10, 2019" "NLnet Labs" "NSD 4.2.4"
.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved.
.\" See LICENSE for the license.
.SH "NAME"
.B nsd
-\- Name Server Daemon (NSD) version 4.2.2.
+\- Name Server Daemon (NSD) version 4.2.4.
.SH "SYNOPSIS"
.B nsd
.RB [ \-4 ]
diff --git a/usr.sbin/nsd/nsd.c b/usr.sbin/nsd/nsd.c
index 9434bff7c9b..e65c8f9dc54 100644
--- a/usr.sbin/nsd/nsd.c
+++ b/usr.sbin/nsd/nsd.c
@@ -119,50 +119,170 @@ version(void)
exit(0);
}
-void
-get_ip_port_frm_str(const char* arg, const char** hostname,
- const char** port)
+static void
+copyaddrinfo(struct nsd_addrinfo *dest, struct addrinfo *src)
+{
+ dest->ai_flags = src->ai_flags;
+ dest->ai_family = src->ai_family;
+ dest->ai_socktype = src->ai_socktype;
+ dest->ai_addrlen = src->ai_addrlen;
+ memcpy(&dest->ai_addr, src->ai_addr, src->ai_addrlen);
+}
+
+static void
+setup_socket(struct nsd_socket *sock, const char *node, const char *port, struct addrinfo *hints)
{
- /* parse src[@port] option */
- char* delim = NULL;
- if (arg) {
- delim = strchr(arg, '@');
+ int ret;
+ char *sep = NULL;
+ char *host, host_buf[INET6_ADDRSTRLEN + 1 /* '\0' */];
+ const char *service;
+ char service_buf[6 + 1 /* '\0' */]; /* 65535 */
+ struct addrinfo *addr = NULL;
+
+ if(node) {
+ host = host_buf;
+ sep = strchr(node, '@');
+ if(sep) {
+ size_t len = (sep - node) + 1;
+ if (len > sizeof(host_buf)) {
+ len = sizeof(host_buf);
+ }
+ strlcpy(host_buf, node, len);
+ strlcpy(service_buf, sep + 1, sizeof(service_buf));
+ service = service_buf;
+ } else {
+ strlcpy(host_buf, node, sizeof(host_buf));
+ service = port;
+ }
+ } else {
+ host = NULL;
+ service = port;
}
- if (delim) {
- *delim = '\0';
- *port = delim+1;
+ if((ret = getaddrinfo(host, service, hints, &addr)) == 0) {
+ copyaddrinfo(&sock->addr, addr);
+ freeaddrinfo(addr);
+ } else {
+ error("cannot parse address '%s': getaddrinfo: %s %s",
+ host ? host : "(null)",
+ gai_strerror(ret),
+ ret==EAI_SYSTEM ? strerror(errno) : "");
}
- *hostname = arg;
}
-/* append interface to interface array (names, udp, tcp) */
-void
-add_interface(char*** nodes, struct nsd* nsd, char* ip)
+static void
+figure_default_sockets(
+ struct nsd_socket **udp, struct nsd_socket **tcp, size_t *ifs,
+ const char *udp_port, const char *tcp_port,
+ const struct addrinfo *hints)
{
- /* realloc the arrays */
- if(nsd->ifs == 0) {
- *nodes = xalloc_zero(sizeof(*nodes));
- nsd->udp = xalloc_zero(sizeof(*nsd->udp));
- nsd->tcp = xalloc_zero(sizeof(*nsd->udp));
- } else {
- region_remove_cleanup(nsd->region, free, *nodes);
- region_remove_cleanup(nsd->region, free, nsd->udp);
- region_remove_cleanup(nsd->region, free, nsd->tcp);
- *nodes = xrealloc(*nodes, (nsd->ifs+1)*sizeof(*nodes));
- nsd->udp = xrealloc(nsd->udp, (nsd->ifs+1)*sizeof(*nsd->udp));
- nsd->tcp = xrealloc(nsd->tcp, (nsd->ifs+1)*sizeof(*nsd->udp));
- (*nodes)[nsd->ifs] = NULL;
- memset(&nsd->udp[nsd->ifs], 0, sizeof(*nsd->udp));
- memset(&nsd->tcp[nsd->ifs], 0, sizeof(*nsd->tcp));
+ int r;
+ size_t i = 0, n = 1;
+ struct addrinfo ai[2] = { *hints, *hints };
+
+ assert(udp != NULL);
+ assert(tcp != NULL);
+ assert(ifs != NULL);
+
+ ai[0].ai_socktype = SOCK_DGRAM;
+ ai[1].ai_socktype = SOCK_STREAM;
+
+#ifdef INET6
+#ifdef IPV6_V6ONLY
+ if (hints->ai_family == AF_UNSPEC) {
+ ai[0].ai_family = AF_INET6;
+ ai[1].ai_family = AF_INET6;
+ n++;
+ }
+#endif /* IPV6_V6ONLY */
+#endif /* INET6 */
+
+ *udp = xalloc_zero((n + 1) * sizeof(struct nsd_socket));
+ *tcp = xalloc_zero((n + 1) * sizeof(struct nsd_socket));
+ region_add_cleanup(nsd.region, free, *udp);
+ region_add_cleanup(nsd.region, free, *tcp);
+
+#ifdef INET6
+ if(hints->ai_family == AF_UNSPEC) {
+ /*
+ * With IPv6 we'd like to open two separate sockets,
+ * one for IPv4 and one for IPv6, both listening to
+ * the wildcard address (unless the -4 or -6 flags are
+ * specified).
+ *
+ * However, this is only supported on platforms where
+ * we can turn the socket option IPV6_V6ONLY _on_.
+ * Otherwise we just listen to a single IPv6 socket
+ * and any incoming IPv4 connections will be
+ * automatically mapped to our IPv6 socket.
+ */
+#ifdef IPV6_V6ONLY
+ struct addrinfo *addrs[2] = { NULL, NULL };
+
+ if((r = getaddrinfo(NULL, udp_port, &ai[0], &addrs[0])) == 0 &&
+ (r = getaddrinfo(NULL, tcp_port, &ai[1], &addrs[1])) == 0)
+ {
+ (*udp)[i].flags |= NSD_SOCKET_IS_OPTIONAL;
+ copyaddrinfo(&(*udp)[i].addr, addrs[0]);
+ (*tcp)[i].flags |= NSD_SOCKET_IS_OPTIONAL;
+ copyaddrinfo(&(*tcp)[i].addr, addrs[1]);
+ i++;
+ } else {
+ log_msg(LOG_WARNING, "No IPv6, fallback to IPv4. getaddrinfo: %s",
+ r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
+ }
+
+ if(addrs[0])
+ freeaddrinfo(addrs[0]);
+ if(addrs[1])
+ freeaddrinfo(addrs[1]);
+
+ ai[0].ai_family = AF_INET;
+ ai[1].ai_family = AF_INET;
+#endif /* IPV6_V6ONLY */
}
- region_add_cleanup(nsd->region, free, *nodes);
- region_add_cleanup(nsd->region, free, nsd->udp);
- region_add_cleanup(nsd->region, free, nsd->tcp);
+#endif /* INET6 */
- /* add it */
- (*nodes)[nsd->ifs] = ip;
- ++nsd->ifs;
+ *ifs = i + 1;
+ setup_socket(&(*udp)[i], NULL, udp_port, &ai[0]);
+ setup_socket(&(*tcp)[i], NULL, tcp_port, &ai[1]);
+}
+
+static void
+figure_sockets(
+ struct nsd_socket **udp, struct nsd_socket **tcp, size_t *ifs,
+ struct ip_address_option *ips,
+ const char *udp_port, const char *tcp_port,
+ const struct addrinfo *hints)
+{
+ size_t i = 0;
+ struct addrinfo ai = *hints;
+ struct ip_address_option *ip;
+
+ if(!ips) {
+ figure_default_sockets(udp, tcp, ifs, udp_port, tcp_port, hints);
+ return;
+ }
+
+ *ifs = 0;
+ for(ip = ips; ip; ip = ip->next) {
+ (*ifs)++;
+ }
+
+ *udp = xalloc_zero((*ifs + 1) * sizeof(struct nsd_socket));
+ *tcp = xalloc_zero((*ifs + 1) * sizeof(struct nsd_socket));
+ region_add_cleanup(nsd.region, free, *udp);
+ region_add_cleanup(nsd.region, free, *tcp);
+
+ ai.ai_flags |= AI_NUMERICHOST;
+ for(ip = ips, i = 0; ip; ip = ip->next, i++) {
+ ai.ai_socktype = SOCK_DGRAM;
+ setup_socket(&(*udp)[i], ip->address, udp_port, &ai);
+ ai.ai_socktype = SOCK_STREAM;
+ setup_socket(&(*tcp)[i], ip->address, tcp_port, &ai);
+ }
+
+ assert(i == *ifs);
}
/*
@@ -212,6 +332,8 @@ writepid(struct nsd *nsd)
{
FILE * fd;
char pidbuf[32];
+ if(!nsd->pidfile || !nsd->pidfile[0])
+ return 0;
snprintf(pidbuf, sizeof(pidbuf), "%lu\n", (unsigned long) nsd->pid);
@@ -244,7 +366,7 @@ unlinkpid(const char* file)
{
int fd = -1;
- if (file) {
+ if (file && file[0]) {
/* truncate pidfile */
fd = open(file, O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
@@ -401,9 +523,8 @@ main(int argc, char *argv[])
struct passwd *pwd = NULL;
#endif /* HAVE_GETPWNAM */
- struct addrinfo hints[2];
- int hints_in_use = 1;
- char** nodes = NULL; /* array of address strings, size nsd.ifs */
+ struct ip_address_option *ip;
+ struct addrinfo hints;
const char *udp_port = 0;
const char *tcp_port = 0;
@@ -419,11 +540,9 @@ main(int argc, char *argv[])
nsd.dbfile = 0;
nsd.pidfile = 0;
nsd.server_kind = NSD_SERVER_MAIN;
- memset(&hints, 0, sizeof(*hints)*2);
- hints[0].ai_family = DEFAULT_AI_FAMILY;
- hints[0].ai_flags = AI_PASSIVE;
- hints[1].ai_family = DEFAULT_AI_FAMILY;
- hints[1].ai_flags = AI_PASSIVE;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = DEFAULT_AI_FAMILY;
+ hints.ai_flags = AI_PASSIVE;
nsd.identity = 0;
nsd.version = VERSION;
nsd.username = 0;
@@ -434,7 +553,6 @@ main(int argc, char *argv[])
nsd.child_count = 0;
nsd.maximum_tcp_count = 0;
nsd.current_tcp_count = 0;
- nsd.grab_ip6_optional = 0;
nsd.file_rotation_ok = 0;
/* Set up our default identity to gethostname(2) */
@@ -447,6 +565,11 @@ main(int argc, char *argv[])
nsd.identity = IDENTITY;
}
+ /* Create region where options will be stored and set defaults */
+ nsd.options = nsd_options_create(region_create_custom(xalloc, free,
+ DEFAULT_CHUNK_SIZE, DEFAULT_LARGE_OBJECT_SIZE,
+ DEFAULT_INITIAL_CLEANUP_SIZE, 1));
+
/* Parse the command line... */
while ((c = getopt(argc, argv, "46a:c:df:hi:I:l:N:n:P:p:s:u:t:X:V:v"
#ifndef NDEBUG /* <mattthijs> only when configured with --enable-checking */
@@ -455,17 +578,22 @@ main(int argc, char *argv[])
)) != -1) {
switch (c) {
case '4':
- hints[0].ai_family = AF_INET;
+ hints.ai_family = AF_INET;
break;
case '6':
#ifdef INET6
- hints[0].ai_family = AF_INET6;
+ hints.ai_family = AF_INET6;
#else /* !INET6 */
error("IPv6 support not enabled.");
#endif /* INET6 */
break;
case 'a':
- add_interface(&nodes, &nsd, optarg);
+ ip = region_alloc_zero(
+ nsd.options->region, sizeof(*ip));
+ ip->address = region_strdup(
+ nsd.options->region, optarg);
+ ip->next = nsd.options->ip_addresses;
+ nsd.options->ip_addresses = ip;
break;
case 'c':
configfile = optarg;
@@ -586,9 +714,6 @@ main(int argc, char *argv[])
error("init tsig failed");
/* Read options */
- nsd.options = nsd_options_create(region_create_custom(xalloc, free,
- DEFAULT_CHUNK_SIZE, DEFAULT_LARGE_OBJECT_SIZE,
- DEFAULT_INITIAL_CLEANUP_SIZE, 1));
if(!parse_options_file(nsd.options, configfile, NULL, NULL)) {
error("could not read config: %s\n", configfile);
}
@@ -597,21 +722,13 @@ main(int argc, char *argv[])
nsd.options->zonelistfile);
}
if(nsd.options->do_ip4 && !nsd.options->do_ip6) {
- hints[0].ai_family = AF_INET;
+ hints.ai_family = AF_INET;
}
#ifdef INET6
if(nsd.options->do_ip6 && !nsd.options->do_ip4) {
- hints[0].ai_family = AF_INET6;
+ hints.ai_family = AF_INET6;
}
#endif /* INET6 */
- if(nsd.options->ip_addresses)
- {
- ip_address_option_type* ip = nsd.options->ip_addresses;
- while(ip) {
- add_interface(&nodes, &nsd, ip->address);
- ip = ip->next;
- }
- }
if (verbosity == 0)
verbosity = nsd.options->verbosity;
#ifndef NDEBUG
@@ -748,73 +865,8 @@ main(int argc, char *argv[])
nsd.this_child = NULL;
- /* We need at least one active interface */
- if (nsd.ifs == 0) {
- add_interface(&nodes, &nsd, NULL);
-
- /*
- * With IPv6 we'd like to open two separate sockets,
- * one for IPv4 and one for IPv6, both listening to
- * the wildcard address (unless the -4 or -6 flags are
- * specified).
- *
- * However, this is only supported on platforms where
- * we can turn the socket option IPV6_V6ONLY _on_.
- * Otherwise we just listen to a single IPv6 socket
- * and any incoming IPv4 connections will be
- * automatically mapped to our IPv6 socket.
- */
-#ifdef INET6
- if (hints[0].ai_family == AF_UNSPEC) {
-#ifdef IPV6_V6ONLY
- add_interface(&nodes, &nsd, NULL);
- hints[0].ai_family = AF_INET6;
- hints[1].ai_family = AF_INET;
- hints_in_use = 2;
- nsd.grab_ip6_optional = 1;
-#else /* !IPV6_V6ONLY */
- hints[0].ai_family = AF_INET6;
-#endif /* IPV6_V6ONLY */
- }
-#endif /* INET6 */
- }
-
- /* Set up the address info structures with real interface/port data */
- assert(nodes);
- for (i = 0; i < nsd.ifs; ++i) {
- int r;
- const char* node = NULL;
- const char* service = NULL;
- int h = ((hints_in_use == 1)?0:i%hints_in_use);
-
- /* We don't perform name-lookups */
- if (nodes[i] != NULL)
- hints[h].ai_flags |= AI_NUMERICHOST;
- get_ip_port_frm_str(nodes[i], &node, &service);
-
- hints[h].ai_socktype = SOCK_DGRAM;
- if ((r=getaddrinfo(node, (service?service:udp_port), &hints[h], &nsd.udp[i].addr)) != 0) {
-#ifdef INET6
- if(nsd.grab_ip6_optional && hints[0].ai_family == AF_INET6) {
- log_msg(LOG_WARNING, "No IPv6, fallback to IPv4. getaddrinfo: %s",
- r==EAI_SYSTEM?strerror(errno):gai_strerror(r));
- continue;
- }
-#endif
- error("cannot parse address '%s': getaddrinfo: %s %s",
- nodes[i]?nodes[i]:"(null)",
- gai_strerror(r),
- r==EAI_SYSTEM?strerror(errno):"");
- }
-
- hints[h].ai_socktype = SOCK_STREAM;
- if ((r=getaddrinfo(node, (service?service:tcp_port), &hints[h], &nsd.tcp[i].addr)) != 0) {
- error("cannot parse address '%s': getaddrinfo: %s %s",
- nodes[i]?nodes[i]:"(null)",
- gai_strerror(r),
- r==EAI_SYSTEM?strerror(errno):"");
- }
- }
+ figure_sockets(&nsd.udp, &nsd.tcp, &nsd.ifs,
+ nsd.options->ip_addresses, udp_port, tcp_port, &hints);
/* Parse the username into uid and gid */
nsd.gid = getgid();
@@ -897,20 +949,22 @@ main(int argc, char *argv[])
log_msg(LOG_NOTICE, "%s starting (%s)", argv0, PACKAGE_STRING);
/* Do we have a running nsd? */
- if ((oldpid = readpid(nsd.pidfile)) == -1) {
- if (errno != ENOENT) {
- log_msg(LOG_ERR, "can't read pidfile %s: %s",
- nsd.pidfile, strerror(errno));
- }
- } else {
- if (kill(oldpid, 0) == 0 || errno == EPERM) {
- log_msg(LOG_WARNING,
- "%s is already running as %u, continuing",
- argv0, (unsigned) oldpid);
+ if(nsd.pidfile && nsd.pidfile[0]) {
+ if ((oldpid = readpid(nsd.pidfile)) == -1) {
+ if (errno != ENOENT) {
+ log_msg(LOG_ERR, "can't read pidfile %s: %s",
+ nsd.pidfile, strerror(errno));
+ }
} else {
- log_msg(LOG_ERR,
- "...stale pid file from process %u",
- (unsigned) oldpid);
+ if (kill(oldpid, 0) == 0 || errno == EPERM) {
+ log_msg(LOG_WARNING,
+ "%s is already running as %u, continuing",
+ argv0, (unsigned) oldpid);
+ } else {
+ log_msg(LOG_ERR,
+ "...stale pid file from process %u",
+ (unsigned) oldpid);
+ }
}
}
@@ -1026,7 +1080,7 @@ main(int argc, char *argv[])
if (nsd.log_filename[0] == '/')
nsd.log_filename += l;
}
- if (nsd.pidfile[0] == '/')
+ if (nsd.pidfile && nsd.pidfile[0] == '/')
nsd.pidfile += l;
if (nsd.dbfile[0] == '/')
nsd.dbfile += l;
diff --git a/usr.sbin/nsd/nsd.conf.5.in b/usr.sbin/nsd/nsd.conf.5.in
index 444c3229c94..addcddeb8cc 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" "Aug 19, 2019" "NLnet Labs" "nsd 4.2.2"
+.TH "nsd.conf" "5" "Dec 10, 2019" "NLnet Labs" "nsd 4.2.4"
.\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved.
.\" See LICENSE for the license.
.SH "NAME"
@@ -100,23 +100,13 @@ with a colon ':'. An attribute is followed by its containing
attributes, or a value.
.P
At the top level only
-.B server:
+.BR server: ,
+.BR key: ,
+.BR pattern: ,
+.BR zone: ,
and
-.B key:
-and
-.B pattern:
-and
-.B zone:
-are allowed. These are followed by their attributes or the start of
-a new
-.B server:
-or
-.B key:
-or
-.B pattern:
-or
-.B zone:
-clause. The
+.B remote-control:
+are allowed. These are followed by their attributes or a new top-level keyword. The
.B zone:
attribute is followed by zone options. The
.B server:
@@ -290,6 +280,8 @@ Use the pid file instead of the platform specific default, usually
.IR @pidfile@.
Same as commandline option
.BR \-P .
+With "" there is no pidfile, for some startup management setups,
+where a pidfile is not useful to have.
.TP
.B port:\fR <number>
Answer queries on the specified port. Default is 53. Same as
@@ -392,6 +384,11 @@ that reduces packets, but exactly to the fragmentation length, the nsd.conf
option reduces packets as small as possible.
The default is yes.
.TP
+.B confine\-to\-zone:\fR <yes or no>
+If set to yes, additional information will not be added to the response if the
+apex zone of the additional information does not match the apex zone of the
+initial query (E.G. CNAME resolution). Default is no.
+.TP
.B refuse\-any:\fR <yes or no>
Refuse queries of type ANY. This is useful to stop query floods trying
to get large responses. Note that rrl ratelimiting also has type ANY as
diff --git a/usr.sbin/nsd/nsd.conf.sample.in b/usr.sbin/nsd/nsd.conf.sample.in
index 32dea906e9b..e6886fd8f1e 100644
--- a/usr.sbin/nsd/nsd.conf.sample.in
+++ b/usr.sbin/nsd/nsd.conf.sample.in
@@ -67,7 +67,7 @@ server:
# The directory for zonefile: files. The daemon chdirs here.
# zonesdir: "@zonesdir@"
-
+
# the list of dynamically added zones.
# zonelistfile: "@zonelistfile@"
@@ -151,6 +151,11 @@ server:
# minimal-responses only emits extra data for referrals.
# minimal-responses: no
+ # Do not return additional information if the apex zone of the
+ # additional information is configured but does not match the apex zone
+ # of the initial query.
+ # confine-to-zone: no
+
# refuse queries of type ANY. For stopping floods.
# refuse-any: no
@@ -193,6 +198,14 @@ server:
# rrl-whitelist-ratelimit: 2000
# RRLend
+ # Service clients over TLS (on the TCP sockets), with plain DNS inside
+ # the TLS stream. Give the certificate to use and private key.
+ # Default is "" (disabled). Requires restart to take effect.
+ # tls-service-key: "path/to/privatekeyfile.key"
+ # tls-service-pem: "path/to/publiccertfile.pem"
+ # tls-service-ocsp: "path/to/ocsp.pem"
+ # tls-port: 853
+
# DNSTAP config section, if compiled with that
# dnstap:
# set this to yes and set one or more of dnstap-log-..-messages to yes.
@@ -205,14 +218,6 @@ server:
# dnstap-log-auth-query-messages: no
# dnstap-log-auth-response-messages: no
- # Service clients over TLS (on the TCP sockets), with plain DNS inside
- # the TLS stream. Give the certificate to use and private key.
- # Default is "" (disabled). Requires restart to take effect.
- # tls-service-key: "path/to/privatekeyfile.key"
- # tls-service-pem: "path/to/publiccertfile.pem"
- # tls-service-ocsp: "path/to/ocsp.pem"
- # tls-port: 853
-
# Remote control config section.
remote-control:
# Enable remote control with nsd-control(8) here.
diff --git a/usr.sbin/nsd/nsd.h b/usr.sbin/nsd/nsd.h
index de3ae8e1d43..0cee8c87fc6 100644
--- a/usr.sbin/nsd/nsd.h
+++ b/usr.sbin/nsd/nsd.h
@@ -110,17 +110,28 @@ typedef unsigned long stc_type;
#define ZTATUP2(nsd, zone, stc, i) /* Nothing */
#endif /* USE_ZONE_STATS */
+#define NSD_SOCKET_IS_OPTIONAL (1<<0)
+
+struct nsd_addrinfo
+{
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ socklen_t ai_addrlen;
+ struct sockaddr_storage ai_addr;
+};
+
struct nsd_socket
{
- struct addrinfo * addr;
+ struct nsd_addrinfo addr;
int s;
- int fam;
+ int flags;
};
struct nsd_child
{
/* The type of child process (UDP or TCP handler). */
- int kind;
+ int kind;
/* The child's process id. */
pid_t pid;
@@ -209,12 +220,11 @@ struct nsd
const char *version;
const char *identity;
uint16_t nsid_len;
- unsigned char *nsid;
+ unsigned char *nsid;
uint8_t file_rotation_ok;
/* number of interfaces */
size_t ifs;
- uint8_t grab_ip6_optional;
/* non0 if so_reuseport is in use, if so, tcp, udp array increased */
int reuseport;
diff --git a/usr.sbin/nsd/options.c b/usr.sbin/nsd/options.c
index 81b80c01f30..5064eca94f8 100644
--- a/usr.sbin/nsd/options.c
+++ b/usr.sbin/nsd/options.c
@@ -24,7 +24,6 @@ int c_parse(void);
int c_lex(void);
int c_wrap(void);
int c_lex_destroy(void);
-void c_error(const char *message);
extern char* c_text;
static int
@@ -68,6 +67,7 @@ nsd_options_create(region_type* region)
opt->log_time_ascii = 1;
opt->round_robin = 0; /* also packet.h::round_robin */
opt->minimal_responses = 1; /* also packet.h::minimal_responses */
+ opt->confine_to_zone = 0;
opt->refuse_any = 1;
opt->server_count = 1;
opt->tcp_count = 100;
@@ -174,19 +174,11 @@ parse_options_file(struct nsd_options* opt, const char* file,
cfg_parser->filename = (char*)file;
cfg_parser->line = 1;
cfg_parser->errors = 0;
- cfg_parser->server_settings_seen = 0;
cfg_parser->opt = opt;
- cfg_parser->current_pattern = 0;
- cfg_parser->current_zone = 0;
- cfg_parser->current_key = 0;
- cfg_parser->current_ip_address_option = opt->ip_addresses;
- while(cfg_parser->current_ip_address_option && cfg_parser->current_ip_address_option->next)
- cfg_parser->current_ip_address_option = cfg_parser->current_ip_address_option->next;
- cfg_parser->current_allow_notify = 0;
- cfg_parser->current_request_xfr = 0;
- cfg_parser->current_notify = 0;
- cfg_parser->current_provide_xfr = 0;
-
+ cfg_parser->pattern = NULL;
+ cfg_parser->zone = NULL;
+ cfg_parser->key = NULL;
+
in = fopen(cfg_parser->filename, "r");
if(!in) {
if(err) {
@@ -205,36 +197,7 @@ parse_options_file(struct nsd_options* opt, const char* file,
fclose(in);
opt->configfile = region_strdup(opt->region, file);
- if(cfg_parser->current_pattern) {
- if(!cfg_parser->current_pattern->pname)
- c_error("last pattern has no name");
- else {
- if(!nsd_options_insert_pattern(cfg_parser->opt,
- cfg_parser->current_pattern))
- c_error("duplicate pattern");
- }
- }
- if(cfg_parser->current_zone) {
- if(!cfg_parser->current_zone->name)
- c_error("last zone has no name");
- else {
- if(!nsd_options_insert_zone(opt,
- cfg_parser->current_zone))
- c_error("duplicate zone");
- }
- if(!cfg_parser->current_zone->pattern)
- c_error("last zone has no pattern");
- }
- if(cfg_parser->current_key)
- {
- if(!cfg_parser->current_key->name)
- c_error("last key has no name");
- if(!cfg_parser->current_key->algorithm)
- c_error("last key has no algorithm");
- if(!cfg_parser->current_key->secret)
- c_error("last key has no secret blob");
- key_options_insert(opt, cfg_parser->current_key);
- }
+
RBTREE_FOR(pat, struct pattern_options*, opt->patterns)
{
/* lookup keys for acls */
@@ -244,7 +207,7 @@ parse_options_file(struct nsd_options* opt, const char* file,
continue;
acl->key_options = key_options_find(opt, acl->key_name);
if(!acl->key_options)
- c_error_msg("key %s in pattern %s could not be found",
+ c_error("key %s in pattern %s could not be found",
acl->key_name, pat->pname);
}
for(acl=pat->notify; acl; acl=acl->next)
@@ -253,7 +216,7 @@ parse_options_file(struct nsd_options* opt, const char* file,
continue;
acl->key_options = key_options_find(opt, acl->key_name);
if(!acl->key_options)
- c_error_msg("key %s in pattern %s could not be found",
+ c_error("key %s in pattern %s could not be found",
acl->key_name, pat->pname);
}
for(acl=pat->request_xfr; acl; acl=acl->next)
@@ -262,7 +225,7 @@ parse_options_file(struct nsd_options* opt, const char* file,
continue;
acl->key_options = key_options_find(opt, acl->key_name);
if(!acl->key_options)
- c_error_msg("key %s in pattern %s could not be found",
+ c_error("key %s in pattern %s could not be found",
acl->key_name, pat->pname);
}
for(acl=pat->provide_xfr; acl; acl=acl->next)
@@ -271,7 +234,7 @@ parse_options_file(struct nsd_options* opt, const char* file,
continue;
acl->key_options = key_options_find(opt, acl->key_name);
if(!acl->key_options)
- c_error_msg("key %s in pattern %s could not be found",
+ c_error("key %s in pattern %s could not be found",
acl->key_name, pat->pname);
}
}
@@ -718,7 +681,7 @@ zone_list_close(struct nsd_options* opt)
}
}
-void
+static void
c_error_va_list_pos(int showpos, const char* fmt, va_list args)
{
char* at = NULL;
@@ -749,35 +712,24 @@ c_error_va_list_pos(int showpos, const char* fmt, va_list args)
}
void
-c_error_msg_pos(int showpos, const char* fmt, ...)
+c_error(const char *fmt, ...)
{
- va_list args;
- va_start(args, fmt);
- c_error_va_list_pos(showpos, fmt, args);
- va_end(args);
-}
+ va_list ap;
+ int showpos = 0;
-void
-c_error_msg(const char* fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
- c_error_va_list_pos(0, fmt, args);
- va_end(args);
-}
+ if (strcmp(fmt, "syntax error") == 0 || strcmp(fmt, "parse error") == 0) {
+ showpos = 1;
+ }
-void
-c_error(const char* str)
-{
- if((strcmp(str, "syntax error")==0 || strcmp(str, "parse error")==0))
- c_error_msg_pos(1, "%s", str);
- else c_error_msg("%s", str);
+ va_start(ap, fmt);
+ c_error_va_list_pos(showpos, fmt, ap);
+ va_end(ap);
}
int
-c_wrap()
+c_wrap(void)
{
- return 1;
+ return 1;
}
struct zone_options*
@@ -1862,11 +1814,11 @@ parse_acl_range_subnet(char* p, void* addr, int maxbits)
int subnet_bits = atoi(p);
uint8_t* addr_bytes = (uint8_t*)addr;
if(subnet_bits == 0 && strcmp(p, "0")!=0) {
- c_error_msg("bad subnet range '%s'", p);
+ c_error("bad subnet range '%s'", p);
return;
}
if(subnet_bits < 0 || subnet_bits > maxbits) {
- c_error_msg("subnet of %d bits out of range [0..%d]", subnet_bits, maxbits);
+ c_error("subnet of %d bits out of range [0..%d]", subnet_bits, maxbits);
return;
}
/* fill addr with n bits of 1s (struct has been zeroed) */
@@ -1908,27 +1860,27 @@ parse_acl_info(region_type* region, char* ip, const char* key)
acl->is_ipv6 = 1;
#ifdef INET6
if(inet_pton(AF_INET6, ip, &acl->addr.addr6) != 1)
- c_error_msg("Bad ip6 address '%s'", ip);
+ c_error("Bad ip6 address '%s'", ip);
if(acl->rangetype==acl_range_mask || acl->rangetype==acl_range_minmax) {
assert(p);
if(inet_pton(AF_INET6, p, &acl->range_mask.addr6) != 1)
- c_error_msg("Bad ip6 address mask '%s'", p);
+ c_error("Bad ip6 address mask '%s'", p);
}
if(acl->rangetype==acl_range_subnet) {
assert(p);
parse_acl_range_subnet(p, &acl->range_mask.addr6, 128);
}
#else
- c_error_msg("encountered IPv6 address '%s'.", ip);
+ c_error("encountered IPv6 address '%s'.", ip);
#endif /* INET6 */
} else {
acl->is_ipv6 = 0;
if(inet_pton(AF_INET, ip, &acl->addr.addr) != 1)
- c_error_msg("Bad ip4 address '%s'", ip);
+ c_error("Bad ip4 address '%s'", ip);
if(acl->rangetype==acl_range_mask || acl->rangetype==acl_range_minmax) {
assert(p);
if(inet_pton(AF_INET, p, &acl->range_mask.addr) != 1)
- c_error_msg("Bad ip4 address mask '%s'", p);
+ c_error("Bad ip4 address mask '%s'", p);
}
if(acl->rangetype==acl_range_subnet) {
assert(p);
@@ -1955,80 +1907,87 @@ parse_acl_info(region_type* region, char* ip, const char* key)
/* copy acl list at end of parser start, update current */
static
-void append_acl(struct acl_options** start, struct acl_options** cur,
- struct acl_options* list)
+void copy_and_append_acls(struct acl_options** start, struct acl_options* list)
{
+ struct acl_options *tail = NULL;
+
+ assert(start != NULL);
+
+ tail = *start;
+ if(tail) {
+ while(tail->next) {
+ tail = tail->next;
+ }
+ }
+
while(list) {
struct acl_options* acl = copy_acl(cfg_parser->opt->region,
list);
acl->next = NULL;
- if(*cur)
- (*cur)->next = acl;
- else *start = acl;
- *cur = acl;
+ if(tail) {
+ tail->next = acl;
+ } else {
+ *start = acl;
+ }
+ tail = acl;
list = list->next;
}
}
void
-config_apply_pattern(const char* name)
+config_apply_pattern(struct pattern_options *dest, const char* name)
{
/* find the pattern */
struct pattern_options* pat = pattern_options_find(cfg_parser->opt,
name);
- struct pattern_options* a = cfg_parser->current_pattern;
if(!pat) {
- c_error_msg("could not find pattern %s", name);
+ c_error("could not find pattern %s", name);
return;
}
/* apply settings */
if(pat->zonefile)
- a->zonefile = region_strdup(cfg_parser->opt->region,
+ dest->zonefile = region_strdup(cfg_parser->opt->region,
pat->zonefile);
if(pat->zonestats)
- a->zonestats = region_strdup(cfg_parser->opt->region,
+ dest->zonestats = region_strdup(cfg_parser->opt->region,
pat->zonestats);
if(!pat->allow_axfr_fallback_is_default) {
- a->allow_axfr_fallback = pat->allow_axfr_fallback;
- a->allow_axfr_fallback_is_default = 0;
+ dest->allow_axfr_fallback = pat->allow_axfr_fallback;
+ dest->allow_axfr_fallback_is_default = 0;
}
if(!pat->notify_retry_is_default) {
- a->notify_retry = pat->notify_retry;
- a->notify_retry_is_default = 0;
+ dest->notify_retry = pat->notify_retry;
+ dest->notify_retry_is_default = 0;
}
if(!pat->max_refresh_time_is_default) {
- a->max_refresh_time = pat->max_refresh_time;
- a->max_refresh_time_is_default = 0;
+ dest->max_refresh_time = pat->max_refresh_time;
+ dest->max_refresh_time_is_default = 0;
}
if(!pat->min_refresh_time_is_default) {
- a->min_refresh_time = pat->min_refresh_time;
- a->min_refresh_time_is_default = 0;
+ dest->min_refresh_time = pat->min_refresh_time;
+ dest->min_refresh_time_is_default = 0;
}
if(!pat->max_retry_time_is_default) {
- a->max_retry_time = pat->max_retry_time;
- a->max_retry_time_is_default = 0;
+ dest->max_retry_time = pat->max_retry_time;
+ dest->max_retry_time_is_default = 0;
}
if(!pat->min_retry_time_is_default) {
- a->min_retry_time = pat->min_retry_time;
- a->min_retry_time_is_default = 0;
+ dest->min_retry_time = pat->min_retry_time;
+ dest->min_retry_time_is_default = 0;
}
- a->size_limit_xfr = pat->size_limit_xfr;
+ dest->size_limit_xfr = pat->size_limit_xfr;
#ifdef RATELIMIT
- a->rrl_whitelist |= pat->rrl_whitelist;
+ dest->rrl_whitelist |= pat->rrl_whitelist;
#endif
/* append acl items */
- append_acl(&a->allow_notify, &cfg_parser->current_allow_notify,
- pat->allow_notify);
- append_acl(&a->request_xfr, &cfg_parser->current_request_xfr,
- pat->request_xfr);
- append_acl(&a->notify, &cfg_parser->current_notify, pat->notify);
- append_acl(&a->provide_xfr, &cfg_parser->current_provide_xfr,
- pat->provide_xfr);
- append_acl(&a->outgoing_interface, &cfg_parser->
- current_outgoing_interface, pat->outgoing_interface);
+ copy_and_append_acls(&dest->allow_notify, pat->allow_notify);
+ copy_and_append_acls(&dest->request_xfr, pat->request_xfr);
+ copy_and_append_acls(&dest->notify, pat->notify);
+ copy_and_append_acls(&dest->provide_xfr, pat->provide_xfr);
+ copy_and_append_acls(&dest->outgoing_interface, pat->outgoing_interface);
if(pat->multi_master_check)
- a->multi_master_check = pat->multi_master_check;
+ dest->multi_master_check = pat->multi_master_check;
}
void
diff --git a/usr.sbin/nsd/options.h b/usr.sbin/nsd/options.h
index 97e2dd07ed8..beb07741c3b 100644
--- a/usr.sbin/nsd/options.h
+++ b/usr.sbin/nsd/options.h
@@ -76,6 +76,7 @@ struct nsd_options {
int server_count;
int tcp_count;
int tcp_reject_overflow;
+ int confine_to_zone;
int tcp_query_count;
int tcp_timeout;
int tcp_mss;
@@ -110,7 +111,7 @@ struct nsd_options {
/* TLS dedicated port */
const char* tls_port;
- /** remote control section. enable toggle. */
+ /** remote control section. enable toggle. */
int control_enable;
/** the interfaces the remote control should listen on */
struct ip_address_option* control_interface;
@@ -298,17 +299,13 @@ struct config_parser_state {
const char* chroot;
int line;
int errors;
- int server_settings_seen;
struct nsd_options* opt;
- struct pattern_options* current_pattern;
- struct zone_options* current_zone;
- struct key_options* current_key;
- struct ip_address_option* current_ip_address_option;
- struct acl_options* current_allow_notify;
- struct acl_options* current_request_xfr;
- struct acl_options* current_notify;
- struct acl_options* current_provide_xfr;
- struct acl_options* current_outgoing_interface;
+ /* pointer to memory where options for the configuration block that is
+ currently parsed must be stored. memory is dynamically allocated,
+ the block is promoted once it is closed. */
+ struct pattern_options *pattern;
+ struct zone_options *zone;
+ struct key_options *key;
void (*err)(void*,const char*);
void* err_arg;
};
@@ -411,8 +408,8 @@ const char* config_make_zonefile(struct zone_options* zone, struct nsd* nsd);
#define ZONEC_PCT_COUNT 100000 /* elements before pct check is done */
/* parsing helpers */
-void c_error(const char* msg);
-void c_error_msg(const char* fmt, ...) ATTR_FORMAT(printf, 1, 2);
+void c_error(const char* msg, ...) ATTR_FORMAT(printf, 1,2);
+int c_wrap(void);
struct acl_options* parse_acl_info(region_type* region, char* ip,
const char* key);
/* true if ipv6 address, false if ipv4 */
@@ -426,6 +423,6 @@ void nsd_options_destroy(struct nsd_options* opt);
/* replace occurrences of one with two in buf, pass length of buffer */
void replace_str(char* buf, size_t len, const char* one, const char* two);
/* apply pattern to the existing pattern in the parser */
-void config_apply_pattern(const char* name);
+void config_apply_pattern(struct pattern_options *dest, const char* name);
#endif /* OPTIONS_H */
diff --git a/usr.sbin/nsd/query.c b/usr.sbin/nsd/query.c
index 8d42a27444b..927d348cab5 100644
--- a/usr.sbin/nsd/query.c
+++ b/usr.sbin/nsd/query.c
@@ -1221,6 +1221,7 @@ answer_lookup_zone(struct nsd *nsd, struct query *q, answer_type *answer,
size_t domain_number, int exact, domain_type *closest_match,
domain_type *closest_encloser, const dname_type *qname)
{
+ zone_type* origzone = q->zone;
q->zone = domain_find_zone(nsd->db, closest_encloser);
if (!q->zone) {
/* no zone for this */
@@ -1235,6 +1236,16 @@ answer_lookup_zone(struct nsd *nsd, struct query *q, answer_type *answer,
RCODE_SET(q->packet, RCODE_SERVFAIL);
return;
}
+
+ /*
+ * If confine-to-zone is set to yes do not return additional
+ * information for a zone with a different apex from the query zone.
+ */
+ if (nsd->options->confine_to_zone &&
+ (origzone != NULL && dname_compare(domain_dname(origzone->apex), domain_dname(q->zone->apex)) != 0)) {
+ return;
+ }
+
/* now move up the closest encloser until it exists, previous
* (possibly empty) closest encloser was useful to finding the zone
* (for empty zones too), but now we want actual data nodes */
diff --git a/usr.sbin/nsd/server.c b/usr.sbin/nsd/server.c
index c78f3b51266..6f5e2b0b189 100644
--- a/usr.sbin/nsd/server.c
+++ b/usr.sbin/nsd/server.c
@@ -11,6 +11,7 @@
#include <sys/types.h>
#include <sys/param.h>
+#include <limits.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/wait.h>
@@ -95,17 +96,17 @@ struct udp_handler_data
{
struct nsd *nsd;
struct nsd_socket *socket;
- query_type *query;
+ struct event event;
};
struct tcp_accept_handler_data {
- struct nsd *nsd;
- struct nsd_socket *socket;
- int event_added;
+ struct nsd *nsd;
+ struct nsd_socket *socket;
+ int event_added;
struct event event;
#ifdef HAVE_SSL
/* handler accepts TLS connections on the dedicated port */
- int tls_accept;
+ int tls_accept;
#endif
};
@@ -114,8 +115,8 @@ struct tcp_accept_handler_data {
* when the number of TCP connection drops below the maximum
* number of TCP connections.
*/
-static size_t tcp_accept_handler_count;
-static struct tcp_accept_handler_data* tcp_accept_handlers;
+static size_t tcp_accept_handler_count;
+static struct tcp_accept_handler_data *tcp_accept_handlers;
static struct event slowaccept_event;
static int slowaccept;
@@ -125,16 +126,25 @@ static unsigned char *ocspdata = NULL;
static long ocspdata_len = 0;
#endif
-#ifndef NONBLOCKING_IS_BROKEN
-# define NUM_RECV_PER_SELECT 100
-#endif
+#ifdef NONBLOCKING_IS_BROKEN
+/* Define NUM_RECV_PER_SELECT to 1 (one) to avoid opportunistically trying to
+ read multiple times from a socket when reported ready by select. */
+# define NUM_RECV_PER_SELECT (1)
+#else /* !NONBLOCKING_IS_BROKEN */
+# define NUM_RECV_PER_SELECT (100)
+#endif /* NONBLOCKING_IS_BROKEN */
-#if (!defined(NONBLOCKING_IS_BROKEN) && defined(HAVE_RECVMMSG))
-struct mmsghdr msgs[NUM_RECV_PER_SELECT];
-struct iovec iovecs[NUM_RECV_PER_SELECT];
-struct query *queries[NUM_RECV_PER_SELECT];
+#ifndef HAVE_MMSGHDR
+struct mmsghdr {
+ struct msghdr msg_hdr;
+ unsigned int msg_len;
+};
#endif
+static struct mmsghdr msgs[NUM_RECV_PER_SELECT];
+static struct iovec iovecs[NUM_RECV_PER_SELECT];
+static struct query *queries[NUM_RECV_PER_SELECT];
+
/*
* Data for the TCP connection handlers.
*
@@ -661,465 +671,507 @@ initialize_dname_compression_tables(struct nsd *nsd)
compressed_dname_offsets[0] = QHEADERSZ; /* The original query name */
}
-/* create and bind sockets. */
static int
-server_init_ifs(struct nsd *nsd, size_t from, size_t to, int* reuseport_works)
+set_reuseport(struct nsd_socket *sock)
{
- struct addrinfo* addr;
- size_t i;
-#if defined(SO_REUSEPORT) || defined(SO_REUSEADDR) || (defined(INET6) && (defined(IPV6_V6ONLY) || defined(IPV6_USE_MIN_MTU) || defined(IPV6_MTU) || defined(IP_TRANSPARENT)) || defined(IP_FREEBIND) || defined(SO_BINDANY))
+#ifdef SO_REUSEPORT
int on = 1;
-#endif
-#ifdef USE_TCP_FASTOPEN
- int qlen;
-#endif
+#ifdef SO_REUSEPORT_LB
+ /* FreeBSD 12 has SO_REUSEPORT_LB that does load balancing like
+ * SO_REUSEPORT on Linux. This is what the users want with the config
+ * option in nsd.conf; if we actually need local address and port reuse
+ * they'll also need to have SO_REUSEPORT set for them, assume it was
+ * _LB they want.
+ */
+ int opt = SO_REUSEPORT_LB;
+ static const char optname[] = "SO_REUSEPORT_LB";
+#else /* !SO_REUSEPORT_LB */
+ int opt = SO_REUSEPORT;
+ static const char optname[] = "SO_REUSEPORT";
+#endif /* SO_REUSEPORT_LB */
+
+ if (0 == setsockopt(sock->s, SOL_SOCKET, opt, &on, sizeof(on))) {
+ return 1;
+ } else if(verbosity >= 3 || errno != ENOPROTOOPT) {
+ log_msg(LOG_ERR, "setsockopt(..., %s, ...) failed: %s",
+ optname, strerror(errno));
+ }
+ return -1;
+#else
+ (void)sock;
+#endif /* SO_REUSEPORT */
- /* UDP */
+ return 0;
+}
- /* Make a socket... */
- for (i = from; i < to; i++) {
- /* for reuseports copy socket specs of first entries */
- addr = nsd->udp[i%nsd->ifs].addr;
- if (!addr) {
- nsd->udp[i].s = -1;
- continue;
- }
- nsd->udp[i].fam = (int)addr->ai_family;
- if ((nsd->udp[i].s = socket(addr->ai_family, addr->ai_socktype, 0)) == -1) {
-#if defined(INET6)
- if (addr->ai_family == AF_INET6 &&
- errno == EAFNOSUPPORT && nsd->grab_ip6_optional) {
- log_msg(LOG_WARNING, "fallback to UDP4, no IPv6: not supported");
- continue;
- }
-#endif /* INET6 */
- log_msg(LOG_ERR, "can't create a socket: %s", strerror(errno));
- return -1;
- }
+static int
+set_reuseaddr(struct nsd_socket *sock)
+{
+#ifdef SO_REUSEADDR
+ int on = 1;
+ if(setsockopt(sock->s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == 0) {
+ return 1;
+ }
+ log_msg(LOG_ERR, "setsockopt(..., SO_REUSEADDR, ...) failed: %s",
+ strerror(errno));
+ return -1;
+#endif /* SO_REUSEADDR */
+ return 0;
+}
-#ifdef SO_REUSEPORT
-# ifdef SO_REUSEPORT_LB
- /* on FreeBSD 12 we have SO_REUSEPORT_LB that does loadbalance
- * like SO_REUSEPORT on Linux. This is what the users want
- * with the config option in nsd.conf; if we actually
- * need local address and port reuse they'll also need to
- * have SO_REUSEPORT set for them, assume it was _LB they want.
- */
- if(nsd->reuseport && *reuseport_works &&
- setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_REUSEPORT_LB,
- (void*)&on, (socklen_t)sizeof(on)) < 0) {
- if(verbosity >= 3
-#ifdef ENOPROTOOPT
- || errno != ENOPROTOOPT
-#endif
- )
- log_msg(LOG_ERR, "setsockopt(..., SO_REUSEPORT_LB, "
- "...) failed: %s", strerror(errno));
- *reuseport_works = 0;
- }
-# else /* SO_REUSEPORT_LB */
- if(nsd->reuseport && *reuseport_works &&
- setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_REUSEPORT,
- (void*)&on, (socklen_t)sizeof(on)) < 0) {
- if(verbosity >= 3
-#ifdef ENOPROTOOPT
- || errno != ENOPROTOOPT
-#endif
- )
- log_msg(LOG_ERR, "setsockopt(..., SO_REUSEPORT, "
- "...) failed: %s", strerror(errno));
- *reuseport_works = 0;
- }
-# endif /* SO_REUSEPORT_LB */
-#else
- (void)reuseport_works;
-#endif /* SO_REUSEPORT */
-#if defined(SO_RCVBUF)
+static int
+set_rcvbuf(struct nsd_socket *sock, int rcv)
+{
+#ifdef SO_RCVBUF
+#ifdef SO_RCVBUFFORCE
+ if(0 == setsockopt(
+ sock->s, SOL_SOCKET, SO_RCVBUFFORCE, &rcv, sizeof(rcv)))
{
- int rcv = 1*1024*1024;
- if (nsd->options->receive_buffer_size > 0) {
- rcv = nsd->options->receive_buffer_size;
- }
-# ifdef SO_RCVBUFFORCE
- if(setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_RCVBUFFORCE, (void*)&rcv,
- (socklen_t)sizeof(rcv)) < 0) {
- if(errno != EPERM && errno != ENOBUFS) {
- log_msg(LOG_ERR, "setsockopt(..., SO_RCVBUFFORCE, "
- "...) failed: %s", strerror(errno));
- return -1;
- }
- }
-# else
- if(setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_RCVBUF, (void*)&rcv,
- (socklen_t)sizeof(rcv)) < 0) {
- if(errno != ENOBUFS && errno != ENOSYS) {
- log_msg(LOG_ERR, "setsockopt(..., SO_RCVBUF, "
- "...) failed: %s", strerror(errno));
- return -1;
- }
- }
-# endif /* SO_RCVBUFFORCE */
+ return 1;
+ }
+ if(errno == EPERM || errno == ENOBUFS) {
+ return 0;
+ }
+ log_msg(LOG_ERR, "setsockopt(..., SO_RCVBUFFORCE, ...) failed: %s",
+ strerror(errno));
+ return -1;
+#else /* !SO_RCVBUFFORCE */
+ if (0 == setsockopt(
+ sock->s, SOL_SOCKET, SO_RCVBUF, &rcv, sizeof(rcv)))
+ {
+ return 1;
}
+ if(errno == ENOSYS || errno == ENOBUFS) {
+ return 0;
+ }
+ log_msg(LOG_ERR, "setsockopt(..., SO_RCVBUF, ...) failed: %s",
+ strerror(errno));
+ return -1;
+#endif /* SO_RCVBUFFORCE */
#endif /* SO_RCVBUF */
+ return 0;
+}
+
+static int
+set_sndbuf(struct nsd_socket *sock, int snd)
+{
#ifdef SO_SNDBUF
+#ifdef SO_SNDBUFFORCE
+ if(0 == setsockopt(
+ sock->s, SOL_SOCKET, SO_SNDBUFFORCE, &snd, sizeof(snd)))
{
- int snd = 1*1024*1024;
- if (nsd->options->send_buffer_size > 0) {
- snd = nsd->options->send_buffer_size;
- }
-# ifdef SO_SNDBUFFORCE
- if(setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_SNDBUFFORCE, (void*)&snd,
- (socklen_t)sizeof(snd)) < 0) {
- if(errno != EPERM && errno != ENOBUFS) {
- log_msg(LOG_ERR, "setsockopt(..., SO_SNDBUFFORCE, "
- "...) failed: %s", strerror(errno));
- return -1;
- }
- }
-# else
- if(setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_SNDBUF, (void*)&snd,
- (socklen_t)sizeof(snd)) < 0) {
- if(errno != ENOBUFS && errno != ENOSYS) {
- log_msg(LOG_ERR, "setsockopt(..., SO_SNDBUF, "
- "...) failed: %s", strerror(errno));
- return -1;
- }
- }
-# endif /* SO_SNDBUFFFORCE */
+ return 1;
+ }
+ if(errno == EPERM || errno == ENOBUFS) {
+ return 0;
+ }
+ log_msg(LOG_ERR, "setsockopt(..., SO_SNDBUFFORCE, ...) failed: %s",
+ strerror(errno));
+ return -1;
+#else /* !SO_SNDBUFFORCE */
+ if(0 == setsockopt(
+ sock->s, SOL_SOCKET, SO_SNDBUF, &snd, sizeof(snd)))
+ {
+ return 1;
+ }
+ if(errno == ENOSYS || errno == ENOBUFS) {
+ return 0;
}
+ log_msg(LOG_ERR, "setsockopt(..., SO_SNDBUF, ...) failed: %s",
+ strerror(errno));
+ return -1;
+#endif /* SO_SNDBUFFORCE */
#endif /* SO_SNDBUF */
-#if defined(INET6)
- if (addr->ai_family == AF_INET6) {
-# if defined(IPV6_V6ONLY)
- if (setsockopt(nsd->udp[i].s,
- IPPROTO_IPV6, IPV6_V6ONLY,
- &on, sizeof(on)) < 0)
- {
- log_msg(LOG_ERR, "setsockopt(..., IPV6_V6ONLY, ...) failed: %s",
- strerror(errno));
- return -1;
- }
-# endif
-# if defined(IPV6_USE_MIN_MTU)
- /*
- * There is no fragmentation of IPv6 datagrams
- * during forwarding in the network. Therefore
- * we do not send UDP datagrams larger than
- * the minimum IPv6 MTU of 1280 octets. The
- * EDNS0 message length can be larger if the
- * network stack supports IPV6_USE_MIN_MTU.
- */
- if (setsockopt(nsd->udp[i].s,
- IPPROTO_IPV6, IPV6_USE_MIN_MTU,
- &on, sizeof(on)) < 0)
- {
- log_msg(LOG_ERR, "setsockopt(..., IPV6_USE_MIN_MTU, ...) failed: %s",
- strerror(errno));
- return -1;
- }
-# elif defined(IPV6_MTU)
- /*
- * On Linux, PMTUD is disabled by default for datagrams
- * so set the MTU equal to the MIN MTU to get the same.
- */
- on = IPV6_MIN_MTU;
- if (setsockopt(nsd->udp[i].s, IPPROTO_IPV6, IPV6_MTU,
- &on, sizeof(on)) < 0)
- {
- log_msg(LOG_ERR, "setsockopt(..., IPV6_MTU, ...) failed: %s",
- strerror(errno));
- return -1;
- }
- on = 1;
-# endif
- }
-#endif
-#if defined(AF_INET)
- if (addr->ai_family == AF_INET) {
-# if defined(IP_MTU_DISCOVER)
- int mtudisc_disabled = 0;
-# if defined(IP_PMTUDISC_OMIT)
- /* Try IP_PMTUDISC_OMIT first */
+ return 0;
+}
- /*
- * Linux 3.15 has IP_PMTUDISC_OMIT which makes sockets
- * ignore PMTU information and send packets with DF=0.
- * Fragmentation is allowed if and only if the packet
- * size exceeds the outgoing interface MTU or the packet
- * encounters smaller MTU link in network.
- * This mitigates DNS fragmentation attacks by preventing
- * forged PMTU information.
- * FreeBSD already has same semantics without setting
- * the option.
- */
- int action_omit = IP_PMTUDISC_OMIT;
- if (!mtudisc_disabled) {
- if(setsockopt(nsd->udp[i].s, IPPROTO_IP,
- IP_MTU_DISCOVER, &action_omit,
- sizeof(action_omit)) < 0)
- {
- log_msg(LOG_ERR, "setsockopt(..., IP_MTU_DISCOVER, IP_PMTUDISC_OMIT...) failed: %s",
- strerror(errno));
- } else {
- mtudisc_disabled = 1;
- }
- }
-# endif /* IP_PMTUDISC_OMIT */
-# if defined(IP_PMTUDISC_DONT)
- /*
- * Use IP_PMTUDISC_DONT
- * if IP_PMTUDISC_OMIT failed / undefined
- */
- if (!mtudisc_disabled) {
- int action_dont = IP_PMTUDISC_DONT;
- if (setsockopt(nsd->udp[i].s, IPPROTO_IP,
- IP_MTU_DISCOVER, &action_dont,
- sizeof(action_dont)) < 0)
- {
- log_msg(LOG_ERR, "setsockopt(..., IP_MTU_DISCOVER, IP_PMTUDISC_DONT...) failed: %s",
- strerror(errno));
- } else {
- mtudisc_disabled = 1;
- }
- }
-# endif /* IP_PMTUDISC_DONT */
- /* exit if all methods to disable PMTUD failed */
- if(!mtudisc_disabled) {
- return -1;
- }
-# elif defined(IP_DONTFRAG)
- int off = 0;
- if (setsockopt(nsd->udp[i].s, IPPROTO_IP, IP_DONTFRAG,
- &off, sizeof(off)) < 0)
- {
- log_msg(LOG_ERR, "setsockopt(..., IP_DONTFRAG, ...) failed: %s",
- strerror(errno));
- return -1;
- }
-# endif
- }
-#endif
- /* set it nonblocking */
- /* otherwise, on OSes with thundering herd problems, the
- UDP recv could block NSD after select returns readable. */
- if (fcntl(nsd->udp[i].s, F_SETFL, O_NONBLOCK) == -1) {
- log_msg(LOG_ERR, "cannot fcntl udp: %s", strerror(errno));
- }
+static int
+set_nonblock(struct nsd_socket *sock)
+{
+ const char *socktype =
+ sock->addr.ai_socktype == SOCK_DGRAM ? "udp" : "tcp";
- /* Bind it... */
- if (nsd->options->ip_freebind) {
-#ifdef IP_FREEBIND
- if (setsockopt(nsd->udp[i].s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)) < 0) {
- log_msg(LOG_ERR, "setsockopt(...,IP_FREEBIND, ...) failed for udp: %s",
- strerror(errno));
- }
-#endif /* IP_FREEBIND */
- }
+ if(fcntl(sock->s, F_SETFL, O_NONBLOCK) == -1) {
+ log_msg(LOG_ERR, "fctnl(..., O_NONBLOCK) failed for %s: %s",
+ socktype, strerror(errno));
+ return -1;
+ }
- if (nsd->options->ip_transparent) {
-#ifdef IP_TRANSPARENT
- if (setsockopt(nsd->udp[i].s, IPPROTO_IP, IP_TRANSPARENT, &on, sizeof(on)) < 0) {
- log_msg(LOG_ERR, "setsockopt(...,IP_TRANSPARENT, ...) failed for udp: %s",
- strerror(errno));
- }
-#endif /* IP_TRANSPARENT */
-#ifdef SO_BINDANY
- if (setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_BINDANY, &on, sizeof(on)) < 0) {
- log_msg(LOG_ERR, "setsockopt(...,SO_BINDANY, ...) failed for udp: %s",
- strerror(errno));
- }
-#endif /* SO_BINDANY */
- }
+ return 1;
+}
- if (
- bind(nsd->udp[i].s, (struct sockaddr *) addr->ai_addr, addr->ai_addrlen) != 0) {
- char buf[256];
- addrport2str((void*)addr->ai_addr, buf, sizeof(buf));
- log_msg(LOG_ERR, "can't bind udp socket %s: %s", buf, strerror(errno));
- return -1;
- }
+static int
+set_ipv6_v6only(struct nsd_socket *sock)
+{
+#ifdef INET6
+#ifdef IPV6_V6ONLY
+ int on = 1;
+ const char *socktype =
+ sock->addr.ai_socktype == SOCK_DGRAM ? "udp" : "tcp";
+
+ if(0 == setsockopt(
+ sock->s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)))
+ {
+ return 1;
}
- /* TCP */
+ log_msg(LOG_ERR, "setsockopt(..., IPV6_V6ONLY, ...) failed for %s: %s",
+ socktype, strerror(errno));
+ return -1;
+#endif /* IPV6_V6ONLY */
+#endif /* INET6 */
-#ifdef USE_TCP_FASTOPEN
- report_tcp_fastopen_config();
+ return 0;
+}
+
+static int
+set_ipv6_use_min_mtu(struct nsd_socket *sock)
+{
+#if defined(INET6) && (defined(IPV6_USE_MIN_MTU) || defined(IPV6_MTU))
+#if defined(IPV6_USE_MIN_MTU)
+ /* There is no fragmentation of IPv6 datagrams during forwarding in the
+ * network. Therefore we do not send UDP datagrams larger than the
+ * minimum IPv6 MTU of 1280 octets. The EDNS0 message length can be
+ * larger if the network stack supports IPV6_USE_MIN_MTU.
+ */
+ int opt = IPV6_USE_MIN_MTU;
+ int optval = 1;
+ static const char optname[] = "IPV6_USE_MIN_MTU";
+#elif defined(IPV6_MTU)
+ /* On Linux, PMTUD is disabled by default for datagrams so set the MTU
+ * to the MIN MTU to get the same.
+ */
+ int opt = IPV6_MTU;
+ int optval = IPV6_MIN_MTU;
+ static const char optname[] = "IPV6_MTU";
#endif
+ if(0 == setsockopt(
+ sock->s, IPPROTO_IPV6, opt, &optval, sizeof(optval)))
+ {
+ return 1;
+ }
- /* Make a socket... */
- for (i = from; i < to; i++) {
- /* for reuseports copy socket specs of first entries */
- addr = nsd->tcp[i%nsd->ifs].addr;
- if (!addr) {
- nsd->tcp[i].s = -1;
- continue;
- }
- nsd->tcp[i].fam = (int)addr->ai_family;
- /* turn off REUSEPORT for TCP by copying the socket fd */
- if(i >= nsd->ifs) {
- nsd->tcp[i].s = nsd->tcp[i%nsd->ifs].s;
- continue;
- }
- if ((nsd->tcp[i].s = socket(addr->ai_family, addr->ai_socktype, 0)) == -1) {
-#if defined(INET6)
- if (addr->ai_family == AF_INET6 &&
- errno == EAFNOSUPPORT && nsd->grab_ip6_optional) {
- log_msg(LOG_WARNING, "fallback to TCP4, no IPv6: not supported");
- continue;
- }
+ log_msg(LOG_ERR, "setsockopt(..., %s, ...) failed: %s",
+ optname, strerror(errno));
+ return -1;
+#else
+ (void)sock;
#endif /* INET6 */
- log_msg(LOG_ERR, "can't create a socket: %s", strerror(errno));
- return -1;
- }
-#ifdef SO_REUSEPORT
- if(nsd->reuseport && *reuseport_works &&
- setsockopt(nsd->tcp[i].s, SOL_SOCKET, SO_REUSEPORT,
- (void*)&on, (socklen_t)sizeof(on)) < 0) {
- if(verbosity >= 3
-#ifdef ENOPROTOOPT
- || errno != ENOPROTOOPT
-#endif
- )
- log_msg(LOG_ERR, "setsockopt(..., SO_REUSEPORT, "
- "...) failed: %s", strerror(errno));
- *reuseport_works = 0;
- }
-#endif /* SO_REUSEPORT */
-#ifdef SO_REUSEADDR
- if (setsockopt(nsd->tcp[i].s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
- log_msg(LOG_ERR, "setsockopt(..., SO_REUSEADDR, ...) failed: %s", strerror(errno));
- }
-#endif /* SO_REUSEADDR */
+ return 0;
+}
-#if defined(INET6)
- if (addr->ai_family == AF_INET6) {
-# if defined(IPV6_V6ONLY)
- if (setsockopt(nsd->tcp[i].s, IPPROTO_IPV6, IPV6_V6ONLY,
- &on, sizeof(on)) < 0) {
- log_msg(LOG_ERR, "setsockopt(..., IPV6_V6ONLY, ...) failed: %s", strerror(errno));
- return -1;
- }
-# endif
-# if defined(IPV6_USE_MIN_MTU)
- /*
- * Use minimum MTU to minimize delays learning working
- * PMTU when communicating through a tunnel.
- */
- if (setsockopt(nsd->tcp[i].s,
- IPPROTO_IPV6, IPV6_USE_MIN_MTU,
- &on, sizeof(on)) < 0) {
- log_msg(LOG_ERR, "setsockopt(..., IPV6_USE_MIN_MTU, ...) failed: %s", strerror(errno));
- return -1;
- }
-# elif defined(IPV6_MTU)
- /*
- * On Linux, PMTUD is disabled by default for datagrams
- * so set the MTU equal to the MIN MTU to get the same.
- */
- on = IPV6_MIN_MTU;
- if (setsockopt(nsd->tcp[i].s, IPPROTO_IPV6, IPV6_MTU,
- &on, sizeof(on)) < 0) {
- log_msg(LOG_ERR, "setsockopt(..., IPV6_MTU, ...) failed: %s", strerror(errno));
- return -1;
- }
- on = 1;
+static int
+set_ipv4_no_pmtu_disc(struct nsd_socket *sock)
+{
+ int ret = 0;
+
+#if defined(IP_MTU_DISCOVER)
+ int opt = IP_MTU_DISCOVER;
+ int optval;
+# if defined(IP_PMTUDISC_OMIT)
+ /* Linux 3.15 has IP_PMTUDISC_OMIT which makes sockets ignore PMTU
+ * information and send packets with DF=0. Fragmentation is allowed if
+ * and only if the packet size exceeds the outgoing interface MTU or
+ * the packet encounters smaller MTU link in network. This mitigates
+ * DNS fragmentation attacks by preventing forged PMTU information.
+ * FreeBSD already has same semantics without setting the option.
+ */
+ optval = IP_PMTUDISC_OMIT;
+ if(0 == setsockopt(
+ sock->s, IPPROTO_IP, opt, &optval, sizeof(optval)))
+ {
+ return 1;
+ }
+
+ log_msg(LOG_ERR, "setsockopt(..., %s, %s, ...) failed: %s",
+ "IP_MTU_DISCOVER", "IP_PMTUDISC_OMIT", strerror(errno));
+# endif /* IP_PMTUDISC_OMIT */
+# if defined(IP_PMTUDISC_DONT)
+ /* Use IP_PMTUDISC_DONT if IP_PMTUDISC_OMIT failed / undefined. */
+ optval = IP_PMTUDISC_DONT;
+ if(0 == setsockopt(
+ sock->s, IPPROTO_IP, opt, &optval, sizeof(optval)))
+ {
+ return 1;
+ }
+
+ log_msg(LOG_ERR, "setsockopt(..., %s, %s, ...) failed: %s",
+ "IP_MTU_DISCOVER", "IP_PMTUDISC_DONT", strerror(errno));
# endif
- }
-#endif
- /* set maximum segment size to tcp socket */
- if(nsd->tcp_mss > 0) {
-#if defined(IPPROTO_TCP) && defined(TCP_MAXSEG)
- if(setsockopt(nsd->tcp[i].s, IPPROTO_TCP, TCP_MAXSEG,
- (void*)&nsd->tcp_mss,
- sizeof(nsd->tcp_mss)) < 0) {
- log_msg(LOG_ERR,
- "setsockopt(...,TCP_MAXSEG,...)"
- " failed for tcp: %s", strerror(errno));
- }
+ ret = -1;
+#elif defined(IP_DONTFRAG)
+ int off = 0;
+ if (0 == setsockopt(
+ sock->s, IPPROTO_IP, IP_DONTFRAG, &off, sizeof(off)))
+ {
+ return 1;
+ }
+
+ log_msg(LOG_ERR, "setsockopt(..., IP_DONTFRAG, ...) failed: %s",
+ strerror(errno));
+ ret = -1;
#else
- log_msg(LOG_ERR, "setsockopt(TCP_MAXSEG) unsupported");
-#endif /* defined(IPPROTO_TCP) && defined(TCP_MAXSEG) */
- }
+ (void)sock;
+#endif
- /* set it nonblocking */
- /* (StevensUNP p463), if tcp listening socket is blocking, then
- it may block in accept, even if select() says readable. */
- if (fcntl(nsd->tcp[i].s, F_SETFL, O_NONBLOCK) == -1) {
- log_msg(LOG_ERR, "cannot fcntl tcp: %s", strerror(errno));
- }
+ return ret;
+}
- /* Bind it... */
- if (nsd->options->ip_freebind) {
+static int
+set_ip_freebind(struct nsd_socket *sock)
+{
#ifdef IP_FREEBIND
- if (setsockopt(nsd->tcp[i].s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)) < 0) {
- log_msg(LOG_ERR, "setsockopt(...,IP_FREEBIND, ...) failed for tcp: %s",
- strerror(errno));
- }
+ int on = 1;
+ const char *socktype =
+ sock->addr.ai_socktype == SOCK_DGRAM ? "udp" : "tcp";
+ if(setsockopt(sock->s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)) == 0)
+ {
+ return 1;
+ }
+ log_msg(LOG_ERR, "setsockopt(..., IP_FREEBIND, ...) failed for %s: %s",
+ socktype, strerror(errno));
+ return -1;
+#else
+ (void)sock;
#endif /* IP_FREEBIND */
- }
- if (nsd->options->ip_transparent) {
-#ifdef IP_TRANSPARENT
- if (setsockopt(nsd->tcp[i].s, IPPROTO_IP, IP_TRANSPARENT, &on, sizeof(on)) < 0) {
- log_msg(LOG_ERR, "setsockopt(...,IP_TRANSPARENT, ...) failed for tcp: %s",
- strerror(errno));
- }
-#endif /* IP_TRANSPARENT */
-#ifdef SO_BINDANY
- if (setsockopt(nsd->tcp[i].s, SOL_SOCKET, SO_BINDANY, &on, sizeof(on)) < 0) {
- log_msg(LOG_ERR, "setsockopt(...,SO_BINDANY, ...) failed for tcp: %s",
- strerror(errno));
- }
-#endif /* SO_BINDANY */
- }
+ return 0;
+}
- if(
- bind(nsd->tcp[i].s, (struct sockaddr *) addr->ai_addr, addr->ai_addrlen) != 0) {
- char buf[256];
- addrport2str((void*)addr->ai_addr, buf, sizeof(buf));
- log_msg(LOG_ERR, "can't bind tcp socket %s: %s", buf, strerror(errno));
- return -1;
- }
+static int
+set_ip_transparent(struct nsd_socket *sock)
+{
+#if defined(IP_TRANSPARENT)
+ int on = 1;
+ const char *socktype =
+ sock->addr.ai_socktype == SOCK_DGRAM ? "udp" : "tcp";
+ if(0 == setsockopt(
+ sock->s, IPPROTO_IP, IP_TRANSPARENT, &on, sizeof(on)))
+ {
+ return 1;
+ }
-#ifdef USE_TCP_FASTOPEN
- /* qlen specifies how many outstanding TFO requests to allow. Limit is a defense
- against IP spoofing attacks as suggested in RFC7413 */
-#ifdef __APPLE__
- /* OS X implementation only supports qlen of 1 via this call. Actual
- value is configured by the net.inet.tcp.fastopen_backlog kernel parm. */
- qlen = 1;
+ log_msg(LOG_ERR, "setsockopt(..., %s, ...) failed for %s: %s",
+ "IP_TRANSPARENT", socktype, strerror(errno));
+ return -1;
+#elif defined(SO_BINDANY)
+ int on = 1;
+ const char *socktype =
+ sock->addr.ai_socktype == SOCK_DGRAM ? "udp" : "tcp";
+ if(0 == setsockopt(
+ sock->s, SOL_SOCKET, SO_BINDANY, &on, sizeof(on)))
+ {
+ return 1;
+ }
+
+ log_msg(LOG_ERR, "setsockopt(..., %s, ...) failed for %s: %s",
+ "SO_BINDANY", socktype, strerror(errno));
+ return -1;
#else
- /* 5 is recommended on linux */
- qlen = 5;
+ (void)sock;
#endif
- if ((setsockopt(nsd->tcp[i].s, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen))) == -1 ) {
-#ifdef ENOPROTOOPT
- /* squelch ENOPROTOOPT: freebsd server mode with kernel support
- disabled, except when verbosity enabled for debugging */
- if(errno != ENOPROTOOPT || verbosity >= 3) {
+
+ return 0;
+}
+
+static int
+set_tcp_maxseg(struct nsd_socket *sock, int mss)
+{
+#if defined(IPPROTO_TCP) && defined(TCP_MAXSEG)
+ if(setsockopt(sock->s, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(mss)) == 0) {
+ return 1;
+ }
+ log_msg(LOG_ERR, "setsockopt(..., TCP_MAXSEG, ...) failed for tcp: %s",
+ strerror(errno));
+ return -1;
+#else
+ log_msg(LOG_ERR, "setsockopt(TCP_MAXSEG) unsupported");
#endif
- if(errno == EPERM) {
- log_msg(LOG_ERR, "Setting TCP Fast Open as server failed: %s ; this could likely be because sysctl net.inet.tcp.fastopen.enabled, net.inet.tcp.fastopen.server_enable, or net.ipv4.tcp_fastopen is disabled", strerror(errno));
- } else {
- log_msg(LOG_ERR, "Setting TCP Fast Open as server failed: %s", strerror(errno));
- }
-#ifdef ENOPROTOOPT
- }
+ return 0;
+}
+
+#ifdef USE_TCP_FASTOPEN
+static int
+set_tcp_fastopen(struct nsd_socket *sock)
+{
+ /* qlen specifies how many outstanding TFO requests to allow. Limit is
+ * a defense against IP spoofing attacks as suggested in RFC7413.
+ */
+ int qlen;
+
+#ifdef __APPLE__
+ /* macOS X implementation only supports qlen of 1 via this call. The
+ * actual value is configured by the net.inet.tcp.fastopen_backlog
+ * kernel parameter.
+ */
+ qlen = 1;
+#else
+ /* 5 is recommended on Linux. */
+ qlen = 5;
#endif
+ if (0 == setsockopt(
+ sock->s, IPPROTO_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen)))
+ {
+ return 1;
+ }
+
+ if (errno == EPERM) {
+ log_msg(LOG_ERR, "Setting TCP Fast Open as server failed: %s "
+ "; this could likely be because sysctl "
+ "net.inet.tcp.fastopen.enabled, "
+ "net.inet.tcp.fastopen.server_enable, or "
+ "net.ipv4.tcp_fastopen is disabled",
+ strerror(errno));
+ /* Squelch ENOPROTOOPT: FreeBSD server mode with kernel support
+ * disabled, except when verbosity enabled for debugging
+ */
+ } else if(errno != ENOPROTOOPT || verbosity >= 3) {
+ log_msg(LOG_ERR, "Setting TCP Fast Open as server failed: %s",
+ strerror(errno));
+ }
+
+ return (errno == ENOPROTOOPT ? 0 : -1);
+}
+#endif /* USE_TCP_FASTOPEN */
+
+static int
+open_udp_socket(struct nsd *nsd, struct nsd_socket *sock, int *reuseport_works)
+{
+ int rcv = 1*1024*1024, snd = 1*1024*1024;
+
+ if(-1 == (sock->s = socket(
+ sock->addr.ai_family, sock->addr.ai_socktype, 0)))
+ {
+#ifdef INET6
+ if((sock->flags & NSD_SOCKET_IS_OPTIONAL) &&
+ (sock->addr.ai_family == AF_INET6) &&
+ (errno == EAFNOSUPPORT))
+ {
+ log_msg(LOG_WARNING, "fallback to UDP4, no IPv6: "
+ "not supported");
+ return 0;
}
#endif
+ log_msg(LOG_ERR, "can't create a socket: %s", strerror(errno));
+ return -1;
+ }
- /* Listen to it... */
- if (listen(nsd->tcp[i].s, TCP_BACKLOG) == -1) {
- log_msg(LOG_ERR, "can't listen: %s", strerror(errno));
+ if(nsd->reuseport && reuseport_works && *reuseport_works)
+ *reuseport_works = (set_reuseport(sock) == 1);
+
+ if(nsd->options->receive_buffer_size > 0)
+ rcv = nsd->options->receive_buffer_size;
+ if(set_rcvbuf(sock, rcv) == -1)
+ return -1;
+
+ if(nsd->options->send_buffer_size > 0)
+ snd = nsd->options->send_buffer_size;
+ if(set_sndbuf(sock, snd) == -1)
+ return -1;
+#ifdef INET6
+ if(sock->addr.ai_family == AF_INET6) {
+ if(set_ipv6_v6only(sock) == -1 ||
+ set_ipv6_use_min_mtu(sock) == -1)
+ return -1;
+ } else
+#endif /* INET6 */
+ if(sock->addr.ai_family == AF_INET) {
+ if(set_ipv4_no_pmtu_disc(sock) == -1)
return -1;
+ }
+
+ /* Set socket to non-blocking. Otherwise, on operating systems
+ * with thundering herd problems, the UDP recv could block
+ * after select returns readable.
+ */
+ set_nonblock(sock);
+
+ if(nsd->options->ip_freebind)
+ (void)set_ip_freebind(sock);
+ if(nsd->options->ip_transparent)
+ (void)set_ip_transparent(sock);
+
+ if(bind(sock->s, (struct sockaddr *)&sock->addr.ai_addr, sock->addr.ai_addrlen) == -1) {
+ char buf[256];
+ addrport2str((void*)&sock->addr.ai_addr, buf, sizeof(buf));
+ log_msg(LOG_ERR, "can't bind udp socket %s: %s",
+ buf, strerror(errno));
+ return -1;
+ }
+
+ return 1;
+}
+
+static int
+open_tcp_socket(struct nsd *nsd, struct nsd_socket *sock, int *reuseport_works)
+{
+#ifdef USE_TCP_FASTOPEN
+ report_tcp_fastopen_config();
+#endif
+
+ (void)reuseport_works;
+
+ if(-1 == (sock->s = socket(
+ sock->addr.ai_family, sock->addr.ai_socktype, 0)))
+ {
+#ifdef INET6
+ if((sock->flags & NSD_SOCKET_IS_OPTIONAL) &&
+ (sock->addr.ai_family == AF_INET6) &&
+ (errno == EAFNOSUPPORT))
+ {
+ log_msg(LOG_WARNING, "fallback to TCP4, no IPv6: "
+ "not supported");
+ return 0;
}
+#endif /* INET6 */
+ log_msg(LOG_ERR, "can't create a socket: %s", strerror(errno));
+ return -1;
}
- return 0;
+ if(nsd->reuseport && reuseport_works && *reuseport_works)
+ *reuseport_works = (set_reuseport(sock) == 1);
+
+ (void)set_reuseaddr(sock);
+
+#ifdef INET6
+ if(sock->addr.ai_family == AF_INET6) {
+ if (set_ipv6_v6only(sock) == -1 ||
+ set_ipv6_use_min_mtu(sock) == -1)
+ return -1;
+ }
+#endif
+
+ if(nsd->tcp_mss > 0)
+ set_tcp_maxseg(sock, nsd->tcp_mss);
+ /* (StevensUNP p463), if TCP listening socket is blocking, then
+ it may block in accept, even if select() says readable. */
+ (void)set_nonblock(sock);
+ if(nsd->options->ip_freebind)
+ (void)set_ip_freebind(sock);
+ if(nsd->options->ip_transparent)
+ (void)set_ip_transparent(sock);
+
+ if(bind(sock->s, (struct sockaddr *)&sock->addr.ai_addr, sock->addr.ai_addrlen) == -1) {
+ char buf[256];
+ addrport2str((void*)&sock->addr.ai_addr, buf, sizeof(buf));
+ log_msg(LOG_ERR, "can't bind tcp socket %s: %s",
+ buf, strerror(errno));
+ return -1;
+ }
+
+#ifdef USE_TCP_FASTOPEN
+ (void)set_tcp_fastopen(sock);
+#endif
+
+ if(listen(sock->s, TCP_BACKLOG) == -1) {
+ log_msg(LOG_ERR, "can't listen: %s", strerror(errno));
+ return -1;
+ }
+
+ return 1;
}
/*
@@ -1128,34 +1180,47 @@ server_init_ifs(struct nsd *nsd, size_t from, size_t to, int* reuseport_works)
int
server_init(struct nsd *nsd)
{
- int reuseport_successful = 1; /* see if reuseport works in OS */
- if(nsd->reuseport) {
- /* increase the size of the udp and tcp interface arrays,
- * there are going to be separate interface file descriptors
- * for every server instance */
- nsd->udp = xrealloc(nsd->udp, (nsd->ifs*nsd->reuseport)*
- sizeof(*nsd->udp));
- nsd->tcp = xrealloc(nsd->tcp, (nsd->ifs*nsd->reuseport)*
- sizeof(*nsd->tcp));
- memset(&nsd->udp[nsd->ifs], 0, sizeof(*nsd->udp)*
- (nsd->ifs*(nsd->reuseport-1)));
- memset(&nsd->tcp[nsd->ifs], 0, sizeof(*nsd->tcp)*
- (nsd->ifs*(nsd->reuseport-1)));
- }
-
- /* open the server interface ports */
- if(server_init_ifs(nsd, 0, nsd->ifs, &reuseport_successful) == -1)
- return -1;
+ size_t i;
+ int reuseport = 1; /* Determine if REUSEPORT works. */
- /* continue to open the remaining reuseport ports */
- if(nsd->reuseport && reuseport_successful) {
- if(server_init_ifs(nsd, nsd->ifs, nsd->ifs*nsd->reuseport,
- &reuseport_successful) == -1)
+ /* open server interface ports */
+ for(i = 0; i < nsd->ifs; i++) {
+ if(open_udp_socket(nsd, &nsd->udp[i], &reuseport) == -1 ||
+ open_tcp_socket(nsd, &nsd->tcp[i], &reuseport) == -1)
+ {
return -1;
- nsd->ifs *= nsd->reuseport;
+ }
+ }
+
+ if(nsd->reuseport && reuseport) {
+ size_t ifs = nsd->ifs * nsd->reuseport;
+
+ /* increase the size of the interface arrays, there are going
+ * to be separate interface file descriptors for every server
+ * instance */
+ region_remove_cleanup(nsd->region, free, nsd->udp);
+ region_remove_cleanup(nsd->region, free, nsd->tcp);
+ nsd->udp = xrealloc(nsd->udp, ifs * sizeof(*nsd->udp));
+ nsd->tcp = xrealloc(nsd->tcp, ifs * sizeof(*nsd->tcp));
+ region_add_cleanup(nsd->region, free, nsd->udp);
+ region_add_cleanup(nsd->region, free, nsd->tcp);
+
+ for(i = nsd->ifs; i < ifs; i++) {
+ nsd->udp[i].addr = nsd->udp[i%nsd->ifs].addr;
+ if(open_udp_socket(nsd, &nsd->udp[i], &reuseport) == -1) {
+ return -1;
+ }
+ /* Turn off REUSEPORT for TCP by copying the socket
+ * file descriptor.
+ */
+ nsd->tcp[i] = nsd->tcp[i%nsd->ifs];
+ }
+
+ nsd->ifs = ifs;
} else {
nsd->reuseport = 0;
}
+
return 0;
}
@@ -1247,8 +1312,6 @@ server_close_all_sockets(struct nsd_socket sockets[], size_t n)
for (i = 0; i < n; ++i) {
if (sockets[i].s != -1) {
close(sockets[i].s);
- if(sockets[i].addr)
- freeaddrinfo(sockets[i].addr);
sockets[i].s = -1;
}
}
@@ -1257,7 +1320,6 @@ server_close_all_sockets(struct nsd_socket sockets[], size_t n)
/*
* Close the sockets, shutdown the server and exit.
* Does not return.
- *
*/
void
server_shutdown(struct nsd *nsd)
@@ -1521,13 +1583,13 @@ server_send_soa_xfrd(struct nsd* nsd, int shortsoa)
}
#ifdef HAVE_SSL
-void
-log_crypto_err(const char* str)
+static void
+log_crypto_from_err(const char* str, unsigned long err)
{
/* error:[error code]:[library name]:[function name]:[reason string] */
char buf[128];
unsigned long e;
- ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+ ERR_error_string_n(err, buf, sizeof(buf));
log_msg(LOG_ERR, "%s crypto %s", str, buf);
while( (e=ERR_get_error()) ) {
ERR_error_string_n(e, buf, sizeof(buf));
@@ -1536,6 +1598,39 @@ log_crypto_err(const char* str)
}
void
+log_crypto_err(const char* str)
+{
+ log_crypto_from_err(str, ERR_get_error());
+}
+
+/** true if the ssl handshake error has to be squelched from the logs */
+static int
+squelch_err_ssl_handshake(unsigned long err)
+{
+ if(verbosity >= 3)
+ return 0; /* only squelch on low verbosity */
+ /* this is very specific, we could filter on ERR_GET_REASON()
+ * (the third element in ERR_PACK) */
+ if(err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_GET_RECORD, SSL_R_HTTPS_PROXY_REQUEST) ||
+ err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_GET_RECORD, SSL_R_HTTP_REQUEST) ||
+ err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_GET_RECORD, SSL_R_WRONG_VERSION_NUMBER) ||
+ err == ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_READ_BYTES, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE)
+#ifdef SSL_F_TLS_POST_PROCESS_CLIENT_HELLO
+ || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_POST_PROCESS_CLIENT_HELLO, SSL_R_NO_SHARED_CIPHER)
+#endif
+#ifdef SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO
+ || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL)
+ || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_UNSUPPORTED_PROTOCOL)
+# ifdef SSL_R_VERSION_TOO_LOW
+ || err == ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_VERSION_TOO_LOW)
+# endif
+#endif
+ )
+ return 1;
+ return 0;
+}
+
+void
perform_openssl_init(void)
{
/* init SSL library */
@@ -1631,7 +1726,7 @@ listen_sslctx_setup_2(void* ctxt)
if(!SSL_CTX_set_ecdh_auto(ctx,1)) {
log_crypto_err("Error in SSL_CTX_ecdh_auto, not enabling ECDHE");
}
-#elif defined(HAVE_DECL_SSL_CTX_SET_TMP_ECDH) && defined(NID_X9_62_prime256v1)
+#elif defined(HAVE_DECL_SSL_CTX_SET_TMP_ECDH) && defined(NID_X9_62_prime256v1) && defined(HAVE_EC_KEY_NEW_BY_CURVE_NAME)
if(1) {
EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1);
if (!ecdh) {
@@ -2504,6 +2599,61 @@ nsd_child_event_base(void)
return base;
}
+static void
+add_udp_handler(
+ struct nsd *nsd,
+ struct nsd_socket *sock,
+ struct udp_handler_data *data)
+{
+ struct event *handler = &data->event;
+
+ data->nsd = nsd;
+ data->socket = sock;
+
+ memset(handler, 0, sizeof(*handler));
+ event_set(handler, sock->s, EV_PERSIST|EV_READ, handle_udp, data);
+ if(event_base_set(nsd->event_base, handler) != 0)
+ log_msg(LOG_ERR, "nsd udp: event_base_set failed");
+ if(event_add(handler, NULL) != 0)
+ log_msg(LOG_ERR, "nsd udp: event_add failed");
+}
+
+void
+add_tcp_handler(
+ struct nsd *nsd,
+ struct nsd_socket *sock,
+ struct tcp_accept_handler_data *data)
+{
+ struct event *handler = &data->event;
+
+ data->nsd = nsd;
+ data->socket = sock;
+
+#ifdef HAVE_SSL
+ if (nsd->tls_ctx &&
+ nsd->options->tls_port &&
+ using_tls_port((struct sockaddr *)&sock->addr.ai_addr, nsd->options->tls_port))
+ {
+ data->tls_accept = 1;
+ if(verbosity >= 2) {
+ char buf[48];
+ addrport2str((struct sockaddr_storage*)&sock->addr.ai_addr, buf, sizeof(buf));
+ VERBOSITY(2, (LOG_NOTICE, "setup TCP for TLS service on interface %s", buf));
+ }
+ } else {
+ data->tls_accept = 0;
+ }
+#endif
+
+ memset(handler, 0, sizeof(*handler));
+ event_set(handler, sock->s, EV_PERSIST|EV_READ, handle_tcp_accept, data);
+ if(event_base_set(nsd->event_base, handler) != 0)
+ log_msg(LOG_ERR, "nsd tcp: event_base_set failed");
+ if(event_add(handler, NULL) != 0)
+ log_msg(LOG_ERR, "nsd tcp: event_add failed");
+ data->event_added = 1;
+}
+
/*
* Serve DNS requests.
*/
@@ -2513,7 +2663,6 @@ server_child(struct nsd *nsd)
size_t i, from, numifs;
region_type *server_region = region_create(xalloc, free);
struct event_base* event_base = nsd_child_event_base();
- query_type *udp_query;
sig_atomic_t mode;
if(!event_base) {
@@ -2569,12 +2718,6 @@ server_child(struct nsd *nsd)
}
if (nsd->server_kind & NSD_SERVER_UDP) {
-#if (defined(NONBLOCKING_IS_BROKEN) || !defined(HAVE_RECVMMSG))
- udp_query = query_create(server_region,
- compressed_dname_offsets, compression_table_size,
- compressed_dnames);
-#else
- udp_query = NULL;
memset(msgs, 0, sizeof(msgs));
for (i = 0; i < NUM_RECV_PER_SELECT; i++) {
queries[i] = query_create(server_region,
@@ -2588,27 +2731,11 @@ server_child(struct nsd *nsd)
msgs[i].msg_hdr.msg_name = &queries[i]->addr;
msgs[i].msg_hdr.msg_namelen = queries[i]->addrlen;
}
-#endif
+
for (i = from; i < from+numifs; ++i) {
- struct udp_handler_data *data;
- struct event *handler;
-
- data = (struct udp_handler_data *) region_alloc(
- server_region,
- sizeof(struct udp_handler_data));
- data->query = udp_query;
- data->nsd = nsd;
- data->socket = &nsd->udp[i];
-
- handler = (struct event*) region_alloc(
- server_region, sizeof(*handler));
- memset(handler, 0, sizeof(*handler));
- event_set(handler, nsd->udp[i].s, EV_PERSIST|EV_READ,
- handle_udp, data);
- if(event_base_set(event_base, handler) != 0)
- log_msg(LOG_ERR, "nsd udp: event_base_set failed");
- if(event_add(handler, NULL) != 0)
- log_msg(LOG_ERR, "nsd udp: event_add failed");
+ struct udp_handler_data *data = region_alloc_zero(
+ nsd->server_region, sizeof(*data));
+ add_udp_handler(nsd, &nsd->udp[i], data);
}
}
@@ -2617,40 +2744,20 @@ server_child(struct nsd *nsd)
* and disable them based on the current number of active TCP
* connections.
*/
- tcp_accept_handler_count = numifs;
- tcp_accept_handlers = (struct tcp_accept_handler_data*)
- region_alloc_array(server_region,
- numifs, sizeof(*tcp_accept_handlers));
if (nsd->server_kind & NSD_SERVER_TCP) {
- for (i = from; i < numifs; ++i) {
- struct event *handler = &tcp_accept_handlers[i-from].event;
- struct tcp_accept_handler_data* data =
+ tcp_accept_handler_count = numifs;
+ tcp_accept_handlers = region_alloc_array(server_region,
+ numifs, sizeof(*tcp_accept_handlers));
+
+ for (i = from; i < numifs; i++) {
+ struct tcp_accept_handler_data *data =
&tcp_accept_handlers[i-from];
- data->nsd = nsd;
- data->socket = &nsd->tcp[i];
-#ifdef HAVE_SSL
- if (nsd->tls_ctx && nsd->options->tls_port && using_tls_port(
- data->socket->addr->ai_addr, nsd->options->tls_port)) {
- data->tls_accept = 1;
- if(verbosity >= 2) {
- char buf[48];
- addrport2str((struct sockaddr_storage*)data->socket->addr->ai_addr, buf, sizeof(buf));
- VERBOSITY(2, (LOG_NOTICE, "setup TCP for TLS service on interface %s", buf));
- }
- }
- else
- data->tls_accept = 0;
-#endif
- memset(handler, 0, sizeof(*handler));
- event_set(handler, nsd->tcp[i].s, EV_PERSIST|EV_READ,
- handle_tcp_accept, data);
- if(event_base_set(event_base, handler) != 0)
- log_msg(LOG_ERR, "nsd tcp: event_base_set failed");
- if(event_add(handler, NULL) != 0)
- log_msg(LOG_ERR, "nsd tcp: event_add failed");
- data->event_added = 1;
+ memset(data, 0, sizeof(*data));
+ add_tcp_handler(nsd, &nsd->tcp[i], data);
}
- } else tcp_accept_handler_count = 0;
+ } else {
+ tcp_accept_handler_count = 0;
+ }
/* The main loop... */
while ((mode = nsd->mode) != NSD_QUIT) {
@@ -2825,7 +2932,97 @@ service_remaining_tcp(struct nsd* nsd)
/* continue to quit after return */
}
-#if defined(HAVE_SENDMMSG) && !defined(NONBLOCKING_IS_BROKEN) && defined(HAVE_RECVMMSG)
+/* Implement recvmmsg and sendmmsg if the platform does not. These functions
+ * are always used, even if nonblocking operations are broken, in which case
+ * NUM_RECV_PER_SELECT is defined to 1 (one).
+ */
+#if defined(HAVE_RECVMMSG)
+#define nsd_recvmmsg recvmmsg
+#else /* !HAVE_RECVMMSG */
+
+static int
+nsd_recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen,
+ int flags, struct timespec *timeout)
+{
+ int orig_errno;
+ unsigned int vpos = 0;
+ ssize_t rcvd;
+
+ /* timeout is ignored, ensure caller does not expect it to work */
+ assert(timeout == NULL);
+
+ orig_errno = errno;
+ errno = 0;
+ while(vpos < vlen) {
+ rcvd = recvfrom(sockfd,
+ msgvec[vpos].msg_hdr.msg_iov->iov_base,
+ msgvec[vpos].msg_hdr.msg_iov->iov_len,
+ flags,
+ msgvec[vpos].msg_hdr.msg_name,
+ &msgvec[vpos].msg_hdr.msg_namelen);
+ if(rcvd < 0) {
+ break;
+ } else {
+ assert((unsigned long long)rcvd <= (unsigned long long)UINT_MAX);
+ msgvec[vpos].msg_len = (unsigned int)rcvd;
+ vpos++;
+ }
+ }
+
+ if(vpos) {
+ /* error will be picked up next time */
+ return (int)vpos;
+ } else if(errno == 0) {
+ errno = orig_errno;
+ return 0;
+ } else if(errno == EAGAIN) {
+ return 0;
+ }
+
+ return -1;
+}
+#endif /* HAVE_RECVMMSG */
+
+#ifdef HAVE_SENDMMSG
+#define nsd_sendmmsg(...) sendmmsg(__VA_ARGS__)
+#else /* !HAVE_SENDMMSG */
+
+static int
+nsd_sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags)
+{
+ int orig_errno;
+ unsigned int vpos = 0;
+ ssize_t snd;
+
+ orig_errno = errno;
+ errno = 0;
+ while(vpos < vlen) {
+ assert(msgvec[vpos].msg_hdr.msg_iovlen == 1);
+ snd = sendto(sockfd,
+ msgvec[vpos].msg_hdr.msg_iov->iov_base,
+ msgvec[vpos].msg_hdr.msg_iov->iov_len,
+ flags,
+ msgvec[vpos].msg_hdr.msg_name,
+ msgvec[vpos].msg_hdr.msg_namelen);
+ if(snd < 0) {
+ break;
+ } else {
+ msgvec[vpos].msg_len = (unsigned int)snd;
+ vpos++;
+ }
+ }
+
+ if(vpos) {
+ return (int)vpos;
+ } else if(errno == 0) {
+ errno = orig_errno;
+ return 0;
+ }
+
+ return -1;
+}
+#endif /* HAVE_SENDMMSG */
+
static void
handle_udp(int fd, short event, void* arg)
{
@@ -2836,7 +3033,7 @@ handle_udp(int fd, short event, void* arg)
if (!(event & EV_READ)) {
return;
}
- recvcount = recvmmsg(fd, msgs, NUM_RECV_PER_SELECT, 0, NULL);
+ recvcount = nsd_recvmmsg(fd, msgs, NUM_RECV_PER_SELECT, 0, NULL);
/* this printf strangely gave a performance increase on Linux */
/* printf("recvcount %d \n", recvcount); */
if (recvcount == -1) {
@@ -2855,7 +3052,12 @@ handle_udp(int fd, short event, void* arg)
q = queries[i];
if (received == -1) {
log_msg(LOG_ERR, "recvmmsg %d failed %s", i, strerror(
- msgs[i].msg_hdr.msg_flags));
+#if defined(HAVE_RECVMMSG)
+ msgs[i].msg_hdr.msg_flags
+#else
+ errno
+#endif
+ ));
STATUP(data->nsd, rxerr);
/* No zone statup */
query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0);
@@ -2866,9 +3068,9 @@ handle_udp(int fd, short event, void* arg)
/* Account... */
#ifdef BIND8_STATS
- if (data->socket->fam == AF_INET) {
+ if (data->socket->addr.ai_family == AF_INET) {
STATUP(data->nsd, qudp);
- } else if (data->socket->fam == AF_INET6) {
+ } else if (data->socket->addr.ai_family == AF_INET6) {
STATUP(data->nsd, qudp6);
}
#endif
@@ -2888,9 +3090,9 @@ handle_udp(int fd, short event, void* arg)
}
#ifdef USE_ZONE_STATS
- if (data->socket->fam == AF_INET) {
+ if (data->socket->addr.ai_family == AF_INET) {
ZTATUP(data->nsd, q->zone, qudp);
- } else if (data->socket->fam == AF_INET6) {
+ } else if (data->socket->addr.ai_family == AF_INET6) {
ZTATUP(data->nsd, q->zone, qudp6);
}
#endif
@@ -2942,12 +3144,20 @@ handle_udp(int fd, short event, void* arg)
/* send until all are sent */
i = 0;
while(i<recvcount) {
- sent = sendmmsg(fd, &msgs[i], recvcount-i, 0);
+ sent = nsd_sendmmsg(fd, &msgs[i], recvcount-i, 0);
if(sent == -1) {
- const char* es = strerror(errno);
- char a[48];
- addr2str(&queries[i]->addr, a, sizeof(a));
- log_msg(LOG_ERR, "sendmmsg [0]=%s count=%d failed: %s", a, (int)(recvcount-i), es);
+ /* don't log transient network full errors, unless
+ * on higher verbosity */
+ if(!(errno == ENOBUFS && verbosity < 1) &&
+#ifdef EWOULDBLOCK
+ !(errno == EWOULDBLOCK && verbosity < 1) &&
+#endif
+ !(errno == EAGAIN && verbosity < 1)) {
+ const char* es = strerror(errno);
+ char a[48];
+ addr2str(&queries[i]->addr, a, sizeof(a));
+ log_msg(LOG_ERR, "sendmmsg [0]=%s count=%d failed: %s", a, (int)(recvcount-i), es);
+ }
#ifdef BIND8_STATS
data->nsd->st.txerr += recvcount-i;
#endif /* BIND8_STATS */
@@ -2962,160 +3172,6 @@ handle_udp(int fd, short event, void* arg)
}
}
-#else /* defined(HAVE_SENDMMSG) && !defined(NONBLOCKING_IS_BROKEN) && defined(HAVE_RECVMMSG) */
-
-static void
-handle_udp(int fd, short event, void* arg)
-{
- struct udp_handler_data *data = (struct udp_handler_data *) arg;
- int received, sent;
-#ifndef NONBLOCKING_IS_BROKEN
-#ifdef HAVE_RECVMMSG
- int recvcount;
-#endif /* HAVE_RECVMMSG */
- int i;
-#endif /* NONBLOCKING_IS_BROKEN */
- struct query *q;
-#if (defined(NONBLOCKING_IS_BROKEN) || !defined(HAVE_RECVMMSG))
- q = data->query;
-#endif
-
- if (!(event & EV_READ)) {
- return;
- }
-#ifndef NONBLOCKING_IS_BROKEN
-#ifdef HAVE_RECVMMSG
- recvcount = recvmmsg(fd, msgs, NUM_RECV_PER_SELECT, 0, NULL);
- /* this printf strangely gave a performance increase on Linux */
- /* printf("recvcount %d \n", recvcount); */
- if (recvcount == -1) {
- if (errno != EAGAIN && errno != EINTR) {
- log_msg(LOG_ERR, "recvmmsg failed: %s", strerror(errno));
- STATUP(data->nsd, rxerr);
- /* No zone statup */
- }
- /* Simply no data available */
- return;
- }
- for (i = 0; i < recvcount; i++) {
- received = msgs[i].msg_len;
- queries[i]->addrlen = msgs[i].msg_hdr.msg_namelen;
- if (received == -1) {
- log_msg(LOG_ERR, "recvmmsg failed");
- STATUP(data->nsd, rxerr);
- /* No zone statup */
- /* the error can be found in msgs[i].msg_hdr.msg_flags */
- query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0);
- iovecs[i].iov_len = buffer_remaining(queries[i]->packet);
- msgs[i].msg_hdr.msg_namelen = queries[i]->addrlen;
- continue;
- }
- q = queries[i];
-#else
- for(i=0; i<NUM_RECV_PER_SELECT; i++) {
-#endif /* HAVE_RECVMMSG */
-#endif /* NONBLOCKING_IS_BROKEN */
-
-#if (defined(NONBLOCKING_IS_BROKEN) || !defined(HAVE_RECVMMSG))
- /* Initialize the query... */
- query_reset(q, UDP_MAX_MESSAGE_LEN, 0);
-
- received = recvfrom(fd,
- buffer_begin(q->packet),
- buffer_remaining(q->packet),
- 0,
- (struct sockaddr *)&q->addr,
- &q->addrlen);
- if (received == -1) {
- if (errno != EAGAIN && errno != EINTR) {
- log_msg(LOG_ERR, "recvfrom failed: %s", strerror(errno));
- STATUP(data->nsd, rxerr);
- /* No zone statup */
- }
- return;
- }
-#endif /* NONBLOCKING_IS_BROKEN || !HAVE_RECVMMSG */
-
- /* Account... */
- if (data->socket->fam == AF_INET) {
- STATUP(data->nsd, qudp);
- } else if (data->socket->fam == AF_INET6) {
- STATUP(data->nsd, qudp6);
- }
-
- buffer_skip(q->packet, received);
- buffer_flip(q->packet);
-#ifdef USE_DNSTAP
- dt_collector_submit_auth_query(data->nsd, &q->addr, q->addrlen,
- q->tcp, q->packet);
-#endif /* USE_DNSTAP */
-
- /* Process and answer the query... */
- if (server_process_query_udp(data->nsd, q) != QUERY_DISCARDED) {
- if (RCODE(q->packet) == RCODE_OK && !AA(q->packet)) {
- STATUP(data->nsd, nona);
- ZTATUP(data->nsd, q->zone, nona);
- }
-
-#ifdef USE_ZONE_STATS
- if (data->socket->fam == AF_INET) {
- ZTATUP(data->nsd, q->zone, qudp);
- } else if (data->socket->fam == AF_INET6) {
- ZTATUP(data->nsd, q->zone, qudp6);
- }
-#endif
-
- /* Add EDNS0 and TSIG info if necessary. */
- query_add_optional(q, data->nsd);
-
- buffer_flip(q->packet);
-
- sent = sendto(fd,
- buffer_begin(q->packet),
- buffer_remaining(q->packet),
- 0,
- (struct sockaddr *) &q->addr,
- q->addrlen);
- if (sent == -1) {
- const char* es = strerror(errno);
- char a[48];
- addr2str(&q->addr, a, sizeof(a));
- log_msg(LOG_ERR, "sendto %s failed: %s", a, es);
- STATUP(data->nsd, txerr);
- ZTATUP(data->nsd, q->zone, txerr);
- } else if ((size_t) sent != buffer_remaining(q->packet)) {
- log_msg(LOG_ERR, "sent %d in place of %d bytes", sent, (int) buffer_remaining(q->packet));
- } else {
-#ifdef BIND8_STATS
- /* Account the rcode & TC... */
- STATUP2(data->nsd, rcode, RCODE(q->packet));
- ZTATUP2(data->nsd, q->zone, rcode, RCODE(q->packet));
- if (TC(q->packet)) {
- STATUP(data->nsd, truncated);
- ZTATUP(data->nsd, q->zone, truncated);
- }
-#endif /* BIND8_STATS */
-#ifdef USE_DNSTAP
- dt_collector_submit_auth_response(data->nsd,
- &q->addr, q->addrlen, q->tcp,
- q->packet, q->zone);
-#endif /* USE_DNSTAP */
- }
- } else {
- STATUP(data->nsd, dropped);
- ZTATUP(data->nsd, q->zone, dropped);
- }
-#ifndef NONBLOCKING_IS_BROKEN
-#ifdef HAVE_RECVMMSG
- query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0);
- iovecs[i].iov_len = buffer_remaining(queries[i]->packet);
- msgs[i].msg_hdr.msg_namelen = queries[i]->addrlen;
-#endif
- }
-#endif
-}
-#endif /* defined(HAVE_SENDMMSG) && !defined(NONBLOCKING_IS_BROKEN) && defined(HAVE_RECVMMSG) */
-
#ifdef HAVE_SSL
/*
* Setup an event for the tcp handler.
@@ -3630,8 +3686,16 @@ tls_handshake(struct tcp_handler_data* data, int fd, int writing)
} else {
if(r == 0)
VERBOSITY(3, (LOG_ERR, "TLS handshake: connection closed prematurely"));
+ else {
+ unsigned long err = ERR_get_error();
+ if(!squelch_err_ssl_handshake(err)) {
+ char a[64], s[256];
+ addr2str(&data->query->addr, a, sizeof(a));
+ snprintf(s, sizeof(s), "TLS handshake failed from %s", a);
+ log_crypto_from_err(s, err);
+ }
+ }
cleanup_tcp_handler(data);
- VERBOSITY(3, (LOG_ERR, "TLS handshake failed"));
return 0;
}
}
diff --git a/usr.sbin/nsd/tsig.c b/usr.sbin/nsd/tsig.c
index a450a8b3029..91ca99b93b5 100644
--- a/usr.sbin/nsd/tsig.c
+++ b/usr.sbin/nsd/tsig.c
@@ -19,7 +19,7 @@
#include "query.h"
#include "rbtree.h"
-#ifndef HAVE_SSL
+#if !defined(HAVE_SSL) || !defined(HAVE_CRYPTO_MEMCMP)
/* we need fixed time compare */
#define CRYPTO_memcmp memcmp_fixedtime
int memcmp_fixedtime(const void *s1, const void *s2, size_t n)
diff --git a/usr.sbin/nsd/xfrd.c b/usr.sbin/nsd/xfrd.c
index 3865747a660..bd07d797676 100644
--- a/usr.sbin/nsd/xfrd.c
+++ b/usr.sbin/nsd/xfrd.c
@@ -430,12 +430,6 @@ xfrd_shutdown()
signal_del(xfrd_sig_evs[i]);
free(xfrd_sig_evs[i]);
}
- for(i=0; i<(int)nsd.ifs; i++) {
- if(nsd.udp[i].s != -1 && nsd.udp[i].addr)
- freeaddrinfo(nsd.udp[i].addr);
- if(nsd.tcp[i].s != -1 && nsd.tcp[i].addr)
- freeaddrinfo(nsd.tcp[i].addr);
- }
}
#ifdef RATELIMIT
rrl_mmap_deinit();
@@ -1843,6 +1837,7 @@ xfrd_parse_received_xfr_packet(xfrd_zone_type* zone, buffer_type* packet,
size_t nscount = NSCOUNT(packet);
int done = 0;
region_type* tempregion = NULL;
+ assert(zone->master);
/* has to be axfr / ixfr reply */
if(!buffer_available(packet, QHEADERSZ)) {
@@ -1863,10 +1858,16 @@ xfrd_parse_received_xfr_packet(xfrd_zone_type* zone, buffer_type* packet,
}
/* check RCODE in all response messages */
if(RCODE(packet) != RCODE_OK) {
- log_msg(LOG_ERR, "xfrd: zone %s received error code %s from "
- "%s",
- zone->apex_str, rcode2str(RCODE(packet)),
- zone->master->ip_address_spec);
+ /* for IXFR failures, do not log unless higher verbosity */
+ if(!(verbosity < 3 && (RCODE(packet) == RCODE_IMPL ||
+ RCODE(packet) == RCODE_FORMAT) &&
+ !zone->master->ixfr_disabled &&
+ !zone->master->use_axfr_only)) {
+ log_msg(LOG_ERR, "xfrd: zone %s received error code %s from "
+ "%s",
+ zone->apex_str, rcode2str(RCODE(packet)),
+ zone->master->ip_address_spec);
+ }
if (RCODE(packet) == RCODE_IMPL ||
RCODE(packet) == RCODE_FORMAT) {
return xfrd_packet_notimpl;