diff options
author | sthen <sthen@openbsd.org> | 2019-12-18 10:58:18 +0000 |
---|---|---|
committer | sthen <sthen@openbsd.org> | 2019-12-18 10:58:18 +0000 |
commit | 5435475d426b8472bbbd30dcd1b34cc38879de70 (patch) | |
tree | 3021abd1c3fb08223d64a3dbeda80aa89a60ddad /usr.sbin/nsd | |
parent | import NSD 4.2.4, ok florian (diff) | |
download | wireguard-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.in | 8 | ||||
-rw-r--r-- | usr.sbin/nsd/config.h.in | 9 | ||||
-rw-r--r-- | usr.sbin/nsd/configlexer.lex | 21 | ||||
-rw-r--r-- | usr.sbin/nsd/configparser.y | 1786 | ||||
-rw-r--r-- | usr.sbin/nsd/configure | 32 | ||||
-rw-r--r-- | usr.sbin/nsd/configure.ac | 9 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-checkconf.8.in | 2 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-checkconf.c | 3 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-checkzone.8.in | 2 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-control.8.in | 2 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd-control.c | 2 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.8.in | 4 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.c | 338 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.conf.5.in | 31 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.conf.sample.in | 23 | ||||
-rw-r--r-- | usr.sbin/nsd/nsd.h | 20 | ||||
-rw-r--r-- | usr.sbin/nsd/options.c | 183 | ||||
-rw-r--r-- | usr.sbin/nsd/options.h | 25 | ||||
-rw-r--r-- | usr.sbin/nsd/query.c | 11 | ||||
-rw-r--r-- | usr.sbin/nsd/server.c | 1426 | ||||
-rw-r--r-- | usr.sbin/nsd/tsig.c | 2 | ||||
-rw-r--r-- | usr.sbin/nsd/xfrd.c | 21 |
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; |