diff options
author | Stuart Henderson <sthen@cvs.openbsd.org> | 2019-09-17 16:19:36 +0000 |
---|---|---|
committer | Stuart Henderson <sthen@cvs.openbsd.org> | 2019-09-17 16:19:36 +0000 |
commit | 98e7ee075697d24b403be25c03393d2bf321d133 (patch) | |
tree | 533e78f0bdd6264595ba7e21ad35a775edbef8ce /usr.sbin | |
parent | 677c7e3573f80df66bf26905b5d3461edec068ce (diff) |
merge 4.2.2
Diffstat (limited to 'usr.sbin')
35 files changed, 1818 insertions, 204 deletions
diff --git a/usr.sbin/nsd/config.h.in b/usr.sbin/nsd/config.h.in index 75c96b60b81..5b141522d5a 100644 --- a/usr.sbin/nsd/config.h.in +++ b/usr.sbin/nsd/config.h.in @@ -76,6 +76,14 @@ /* if time.h provides ctime_r prototype */ #undef HAVE_CTIME_R_PROTO +/* Define to 1 if you have the declaration of `SSL_CTX_set_ecdh_auto', and to + 0 if you don't. */ +#undef HAVE_DECL_SSL_CTX_SET_ECDH_AUTO + +/* Define to 1 if you have the declaration of `SSL_CTX_set_tmp_ecdh', and to 0 + if you don't. */ +#undef HAVE_DECL_SSL_CTX_SET_TMP_ECDH + /* Define to 1 if you have the `dup2' function. */ #undef HAVE_DUP2 @@ -221,6 +229,9 @@ /* Define to 1 if you have the `OPENSSL_init_ssl' function. */ #undef HAVE_OPENSSL_INIT_SSL +/* Define to 1 if you have the <openssl/ocsp.h> header file. */ +#undef HAVE_OPENSSL_OCSP_H + /* Define to 1 if you have the <openssl/rand.h> header file. */ #undef HAVE_OPENSSL_RAND_H @@ -239,7 +250,7 @@ /* Define to 1 if you have the `pwrite' function. */ #undef HAVE_PWRITE -/* Define to 1 if you have the `reallocarray' function. */ +/* If we have reallocarray(3) */ #undef HAVE_REALLOCARRAY /* Define if recvmmsg is implemented */ @@ -281,6 +292,9 @@ /* Define if you have the SSL libraries installed. */ #undef HAVE_SSL +/* Define to 1 if you have the `SSL_CTX_set_security_level' function. */ +#undef HAVE_SSL_CTX_SET_SECURITY_LEVEL + /* Define to 1 if you have the <stdarg.h> header file. */ #undef HAVE_STDARG_H @@ -507,6 +521,9 @@ /* Define to the default tcp timeout. */ #undef TCP_TIMEOUT +/* Define to the default DNS over TLS port. */ +#undef TLS_PORT + /* Define to the default maximum udp message length. */ #undef UDP_MAX_MESSAGE_LEN @@ -550,6 +567,9 @@ #endif +/* Define this to enable TCP fast open. */ +#undef USE_TCP_FASTOPEN + /* Define this to enable per-zone statistics gathering. */ #undef USE_ZONE_STATS @@ -688,6 +708,9 @@ # ifndef _BSD_SOURCE # define _BSD_SOURCE 1 # endif +# ifndef _OPENBSD_SOURCE +# define _OPENBSD_SOURCE 1 +# endif # ifndef _DEFAULT_SOURCE # define _DEFAULT_SOURCE 1 # endif diff --git a/usr.sbin/nsd/configlexer.lex b/usr.sbin/nsd/configlexer.lex index ead1b96fa80..0ea97969139 100644 --- a/usr.sbin/nsd/configlexer.lex +++ b/usr.sbin/nsd/configlexer.lex @@ -201,9 +201,12 @@ ip-address{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_ADDRESS;} interface{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_ADDRESS;} ip-transparent{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_TRANSPARENT;} ip-freebind{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP_FREEBIND;} +send-buffer-size{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SEND_BUFFER_SIZE;} +receive-buffer-size{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RECEIVE_BUFFER_SIZE;} debug-mode{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DEBUG_MODE;} use-systemd{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_USE_SYSTEMD;} hide-version{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_HIDE_VERSION;} +hide-identity{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_HIDE_IDENTITY;} ip4-only{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP4_ONLY;} ip6-only{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_IP6_ONLY;} do-ip4{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DO_IP4;} @@ -215,6 +218,7 @@ nsid{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_NSID;} logfile{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_LOGFILE;} server-count{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_SERVER_COUNT;} tcp-count{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_COUNT;} +tcp-reject-overflow{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_REJECT_OVERFLOW;} tcp-query-count{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_QUERY_COUNT;} tcp-timeout{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_TIMEOUT;} tcp-mss{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TCP_MSS;} @@ -287,6 +291,10 @@ min-refresh-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_REFRESH_TIM max-retry-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MAX_RETRY_TIME;} min-retry-time{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MIN_RETRY_TIME;} multi-master-check{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_MULTI_MASTER_CHECK;} +tls-service-key{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TLS_SERVICE_KEY;} +tls-service-ocsp{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TLS_SERVICE_OCSP;} +tls-service-pem{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TLS_SERVICE_PEM;} +tls-port{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_TLS_PORT;} {NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++;} /* Quoted strings. Strip leading and ending quotes */ diff --git a/usr.sbin/nsd/configparser.y b/usr.sbin/nsd/configparser.y index 1e4d75e9a47..f1520e0bf90 100644 --- a/usr.sbin/nsd/configparser.y +++ b/usr.sbin/nsd/configparser.y @@ -59,7 +59,7 @@ extern config_parser_state_type* cfg_parser; %token VAR_KEY %token VAR_ALGORITHM VAR_SECRET %token VAR_AXFR VAR_UDP -%token VAR_VERBOSITY VAR_HIDE_VERSION +%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 @@ -76,6 +76,9 @@ extern config_parser_state_type* cfg_parser; %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 +%token VAR_TCP_REJECT_OVERFLOW %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; @@ -107,7 +110,11 @@ content_server: server_ip_address | server_ip_transparent | server_debug_mode | 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_minimal_responses | server_refuse_any | server_use_systemd; + 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)); @@ -138,14 +145,32 @@ server_ip_transparent: VAR_IP_TRANSPARENT STRING 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)); +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)); @@ -175,6 +200,14 @@ server_hide_version: VAR_HIDE_VERSION STRING 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 */ @@ -339,6 +372,17 @@ server_tcp_count: VAR_TCP_COUNT STRING 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)); @@ -529,7 +573,30 @@ server_zonefiles_write: VAR_ZONEFILES_WRITE STRING 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")); @@ -714,7 +781,7 @@ zone_config_item: zone_zonefile | zone_allow_notify | zone_request_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; + zone_size_limit_xfr | zone_multi_master_check; pattern_name: VAR_NAME STRING { OUTYY(("P(pattern_name:%s)\n", $2)); diff --git a/usr.sbin/nsd/configure b/usr.sbin/nsd/configure index 88a1f1d8af5..c4c9a63811b 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.1.27. +# Generated by GNU Autoconf 2.69 for NSD 4.2.2. # # 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.1.27' -PACKAGE_STRING='NSD 4.1.27' +PACKAGE_VERSION='4.2.2' +PACKAGE_STRING='NSD 4.2.2' PACKAGE_BUGREPORT='nsd-bugs@nlnetlabs.nl' PACKAGE_URL='' @@ -744,6 +744,7 @@ with_dnstap_socket_path with_protobuf_c with_libfstrm enable_systemd +enable_tcp_fastopen ' ac_precious_vars='build_alias host_alias @@ -1296,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.1.27 to adapt to many kinds of systems. +\`configure' configures NSD 4.2.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1357,7 +1358,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of NSD 4.1.27:";; + short | recursive ) echo "Configuration of NSD 4.2.2:";; esac cat <<\_ACEOF @@ -1398,6 +1399,7 @@ Optional Features: but unaligned reads. --enable-dnstap Enable dnstap support (requires fstrm, protobuf-c) --enable-systemd compile with systemd support + --enable-tcp-fastopen Enable TCP Fast Open Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -1512,7 +1514,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -NSD configure 4.1.27 +NSD configure 4.2.2 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2221,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.1.27, which was +It was created by NSD $as_me 4.2.2, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -8340,20 +8342,44 @@ esac fi -ac_fn_c_check_func "$LINENO" "reallocarray" "ac_cv_func_reallocarray" -if test "x$ac_cv_func_reallocarray" = xyes; then : - $as_echo "#define HAVE_REALLOCARRAY 1" >>confdefs.h +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for reallocarray" >&5 +$as_echo_n "checking for reallocarray... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + +#ifndef _OPENBSD_SOURCE +#define _OPENBSD_SOURCE 1 +#endif +#include <stdlib.h> +int main(void) { + void* p = reallocarray(NULL, 10, 100); + free(p); + return 0; +} + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define HAVE_REALLOCARRAY 1" >>confdefs.h + else - case " $LIBOBJS " in + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + case " $LIBOBJS " in *" reallocarray.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS reallocarray.$ac_objext" ;; esac -fi - +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext # # Check for b64_ntop (and assume result applies to b64_pton as well). @@ -8536,6 +8562,11 @@ _ACEOF cat >>confdefs.h <<_ACEOF +#define TLS_PORT "853" +_ACEOF + + +cat >>confdefs.h <<_ACEOF #define MAXSYSLOGMSGLEN 512 _ACEOF @@ -9104,56 +9135,93 @@ fi fi SSL_LIBS="-lssl" - for ac_header in openssl/ssl.h + for ac_header in openssl/ssl.h openssl/err.h openssl/rand.h openssl/ocsp.h do : - ac_fn_c_check_header_compile "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " -if test "x$ac_cv_header_openssl_ssl_h" = xyes; then : +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF -#define HAVE_OPENSSL_SSL_H 1 +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done - for ac_header in openssl/err.h + for ac_func in HMAC_CTX_reset HMAC_CTX_new EVP_cleanup ERR_load_crypto_strings OPENSSL_init_crypto SSL_CTX_set_security_level do : - ac_fn_c_check_header_compile "$LINENO" "openssl/err.h" "ac_cv_header_openssl_err_h" "$ac_includes_default -" -if test "x$ac_cv_header_openssl_err_h" = xyes; then : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF -#define HAVE_OPENSSL_ERR_H 1 +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi - done - for ac_header in openssl/rand.h -do : - ac_fn_c_check_header_compile "$LINENO" "openssl/rand.h" "ac_cv_header_openssl_rand_h" "$ac_includes_default + ac_fn_c_check_decl "$LINENO" "SSL_CTX_set_ecdh_auto" "ac_cv_have_decl_SSL_CTX_set_ecdh_auto" " +$ac_includes_default +#ifdef HAVE_OPENSSL_ERR_H +#include <openssl/err.h> +#endif + +#ifdef HAVE_OPENSSL_RAND_H +#include <openssl/rand.h> +#endif + +#ifdef HAVE_OPENSSL_CONF_H +#include <openssl/conf.h> +#endif + +#ifdef HAVE_OPENSSL_ENGINE_H +#include <openssl/engine.h> +#endif +#include <openssl/ssl.h> +#include <openssl/evp.h> + " -if test "x$ac_cv_header_openssl_rand_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_OPENSSL_RAND_H 1 +if test "x$ac_cv_have_decl_SSL_CTX_set_ecdh_auto" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_SSL_CTX_SET_ECDH_AUTO $ac_have_decl _ACEOF +ac_fn_c_check_decl "$LINENO" "SSL_CTX_set_tmp_ecdh" "ac_cv_have_decl_SSL_CTX_set_tmp_ecdh" " +$ac_includes_default +#ifdef HAVE_OPENSSL_ERR_H +#include <openssl/err.h> +#endif -fi +#ifdef HAVE_OPENSSL_RAND_H +#include <openssl/rand.h> +#endif -done +#ifdef HAVE_OPENSSL_CONF_H +#include <openssl/conf.h> +#endif - for ac_func in HMAC_CTX_reset HMAC_CTX_new EVP_cleanup ERR_load_crypto_strings OPENSSL_init_crypto -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" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF +#ifdef HAVE_OPENSSL_ENGINE_H +#include <openssl/engine.h> +#endif +#include <openssl/ssl.h> +#include <openssl/evp.h> +" +if test "x$ac_cv_have_decl_SSL_CTX_set_tmp_ecdh" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 fi -done + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_SSL_CTX_SET_TMP_ECDH $ac_have_decl +_ACEOF + BAKLIBS="$LIBS" @@ -9174,6 +9242,8 @@ done else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: No SSL, therefore remote-control is disabled" >&5 $as_echo "$as_me: WARNING: No SSL, therefore remote-control is disabled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: No SSL, therefore TLS is disabled" >&5 +$as_echo "$as_me: WARNING: No SSL, therefore TLS is disabled" >&2;} fi # Check whether --enable-nsec3 was given. @@ -9648,6 +9718,33 @@ fi # Include systemd.m4 - end +# Check whether --enable-tcp-fastopen was given. +if test "${enable_tcp_fastopen+set}" = set; then : + enableval=$enable_tcp_fastopen; +fi + +case "$enable_tcp_fastopen" in + yes) + ac_fn_c_check_decl "$LINENO" "TCP_FASTOPEN" "ac_cv_have_decl_TCP_FASTOPEN" "$ac_includes_default +#include <netinet/tcp.h> + +" +if test "x$ac_cv_have_decl_TCP_FASTOPEN" = xyes; then : + +else + as_fn_error $? "TCP Fast Open is not available: please rerun without --enable-tcp-fastopen" "$LINENO" 5 +fi + + +cat >>confdefs.h <<_ACEOF +#define USE_TCP_FASTOPEN 1 +_ACEOF + + ;; + no|*) + ;; +esac + @@ -10194,7 +10291,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.1.27, which was +This file was extended by NSD $as_me 4.2.2, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -10256,7 +10353,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.1.27 +NSD config.status 4.2.2 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 f2e384c8443..e49fb3c3e9d 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.1.27,nsd-bugs@nlnetlabs.nl) +AC_INIT(NSD,4.2.2,nsd-bugs@nlnetlabs.nl) AC_CONFIG_HEADER([config.h]) CFLAGS="$CFLAGS" @@ -689,7 +689,24 @@ AC_REPLACE_FUNCS(strlcpy) AC_REPLACE_FUNCS(strptime) AC_REPLACE_FUNCS(pselect) AC_REPLACE_FUNCS(memmove) -AC_REPLACE_FUNCS(reallocarray) +AC_MSG_CHECKING([for reallocarray]) +AC_LINK_IFELSE([AC_LANG_SOURCE(AC_INCLUDES_DEFAULT +[[ +#ifndef _OPENBSD_SOURCE +#define _OPENBSD_SOURCE 1 +#endif +#include <stdlib.h> +int main(void) { + void* p = reallocarray(NULL, 10, 100); + free(p); + return 0; +} +]])], [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_REALLOCARRAY, 1, [If we have reallocarray(3)]) +], [ + AC_MSG_RESULT(no) + AC_LIBOBJ(reallocarray) +]) # # Check for b64_ntop (and assume result applies to b64_pton as well). @@ -755,6 +772,7 @@ AC_DEFINE_UNQUOTED([TCP_MAX_MESSAGE_LEN], [65535], [Define to the default maximu AC_DEFINE_UNQUOTED([UDP_PORT], ["53"], [Define to the default udp port.]) AC_DEFINE_UNQUOTED([UDP_MAX_MESSAGE_LEN], [512], [Define to the default maximum udp message length.]) AC_DEFINE_UNQUOTED([EDNS_MAX_MESSAGE_LEN], [4096], [Define to the default maximum message length with EDNS.]) +AC_DEFINE_UNQUOTED([TLS_PORT], ["853"], [Define to the default DNS over TLS port.]) AC_DEFINE_UNQUOTED([MAXSYSLOGMSGLEN], [512], [Define to the maximum message length to pass to syslog.]) AC_DEFINE_UNQUOTED([NSD_CONTROL_PORT], [8952], [Define to the default nsd-control port.]) AC_DEFINE_UNQUOTED([NSD_CONTROL_VERSION], [1], [Define to nsd-control proto version.]) @@ -893,10 +911,29 @@ if test x$HAVE_SSL = x"yes"; then fi SSL_LIBS="-lssl" AC_SUBST(SSL_LIBS) - AC_CHECK_HEADERS([openssl/ssl.h],,, [AC_INCLUDES_DEFAULT]) - AC_CHECK_HEADERS([openssl/err.h],,, [AC_INCLUDES_DEFAULT]) - AC_CHECK_HEADERS([openssl/rand.h],,, [AC_INCLUDES_DEFAULT]) - AC_CHECK_FUNCS([HMAC_CTX_reset HMAC_CTX_new EVP_cleanup ERR_load_crypto_strings OPENSSL_init_crypto]) + 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_DECLS([SSL_CTX_set_ecdh_auto,SSL_CTX_set_tmp_ecdh], [], [], [ +AC_INCLUDES_DEFAULT +#ifdef HAVE_OPENSSL_ERR_H +#include <openssl/err.h> +#endif + +#ifdef HAVE_OPENSSL_RAND_H +#include <openssl/rand.h> +#endif + +#ifdef HAVE_OPENSSL_CONF_H +#include <openssl/conf.h> +#endif + +#ifdef HAVE_OPENSSL_ENGINE_H +#include <openssl/engine.h> +#endif +#include <openssl/ssl.h> +#include <openssl/evp.h> +]) + BAKLIBS="$LIBS" LIBS="-lssl $LIBS" @@ -905,6 +942,7 @@ if test x$HAVE_SSL = x"yes"; then else AC_MSG_WARN([No SSL, therefore remote-control is disabled]) + AC_MSG_WARN([No SSL, therefore TLS is disabled]) fi AC_ARG_ENABLE(nsec3, AC_HELP_STRING([--disable-nsec3], [Disable NSEC3 support])) @@ -987,6 +1025,18 @@ dt_DNSTAP([${localstatedir}/run/nsd-dnstap.sock], sinclude(systemd.m4) # Include systemd.m4 - end +AC_ARG_ENABLE(tcp-fastopen, AC_HELP_STRING([--enable-tcp-fastopen], [Enable TCP Fast Open])) +case "$enable_tcp_fastopen" in + yes) + AC_CHECK_DECL([TCP_FASTOPEN], [], [AC_MSG_ERROR([TCP Fast Open is not available: please rerun without --enable-tcp-fastopen])], [AC_INCLUDES_DEFAULT +#include <netinet/tcp.h> + ]) + AC_DEFINE_UNQUOTED([USE_TCP_FASTOPEN], [1], [Define this to enable TCP fast open.]) + ;; + no|*) + ;; +esac + AH_BOTTOM([ /* define before includes as it specifies what standard to use. */ #if (defined(HAVE_PSELECT) && !defined (HAVE_PSELECT_PROTO)) \ @@ -1001,6 +1051,9 @@ AH_BOTTOM([ # ifndef _BSD_SOURCE # define _BSD_SOURCE 1 # endif +# ifndef _OPENBSD_SOURCE +# define _OPENBSD_SOURCE 1 +# endif # ifndef _DEFAULT_SOURCE # define _DEFAULT_SOURCE 1 # endif diff --git a/usr.sbin/nsd/dns.c b/usr.sbin/nsd/dns.c index ad0d9245d49..a5e4ad4c9a0 100644 --- a/usr.sbin/nsd/dns.c +++ b/usr.sbin/nsd/dns.c @@ -121,10 +121,10 @@ static rrtype_descriptor_type rrtype_descriptors[(RRTYPE_DESCRIPTORS_LENGTH+1)] { TYPE_SIG, "SIG", T_SIG, 9, 9, { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_LONG, RDATA_WF_LONG, RDATA_WF_LONG, RDATA_WF_SHORT, - RDATA_WF_UNCOMPRESSED_DNAME, RDATA_WF_BINARY }, - { RDATA_ZF_RRTYPE, RDATA_ZF_BYTE, RDATA_ZF_BYTE, RDATA_ZF_PERIOD, - RDATA_ZF_TIME, RDATA_ZF_TIME, RDATA_ZF_SHORT, RDATA_ZF_DNAME, - RDATA_ZF_BASE64 } }, + RDATA_WF_LITERAL_DNAME, RDATA_WF_BINARY }, + { RDATA_ZF_RRTYPE, RDATA_ZF_ALGORITHM, RDATA_ZF_BYTE, RDATA_ZF_PERIOD, + RDATA_ZF_TIME, RDATA_ZF_TIME, RDATA_ZF_SHORT, + RDATA_ZF_LITERAL_DNAME, RDATA_ZF_BASE64 } }, /* 25 */ { TYPE_KEY, "KEY", T_KEY, 4, 4, { RDATA_WF_SHORT, RDATA_WF_BYTE, RDATA_WF_BYTE, RDATA_WF_BINARY }, diff --git a/usr.sbin/nsd/ipc.c b/usr.sbin/nsd/ipc.c index 46feb0a0129..190e30a0cf0 100644 --- a/usr.sbin/nsd/ipc.c +++ b/usr.sbin/nsd/ipc.c @@ -37,6 +37,7 @@ ipc_child_quit(struct nsd* nsd) { /* call shutdown and quit routines */ nsd->mode = NSD_QUIT; + service_remaining_tcp(nsd); #ifdef BIND8_STATS bind8_stats(nsd); #endif /* BIND8_STATS */ @@ -267,6 +268,8 @@ stats_add(struct nsdst* total, struct nsdst* s) total->qudp6 += s->qudp6; total->ctcp += s->ctcp; total->ctcp6 += s->ctcp6; + total->ctls += s->ctls; + total->ctls6 += s->ctls6; for(i=0; i<sizeof(total->rcode)/sizeof(stc_type); i++) total->rcode[i] += s->rcode[i]; for(i=0; i<sizeof(total->opcode)/sizeof(stc_type); i++) @@ -298,6 +301,8 @@ stats_subtract(struct nsdst* total, struct nsdst* s) total->qudp6 -= s->qudp6; total->ctcp -= s->ctcp; total->ctcp6 -= s->ctcp6; + total->ctls -= s->ctls; + total->ctls6 -= s->ctls6; for(i=0; i<sizeof(total->rcode)/sizeof(stc_type); i++) total->rcode[i] -= s->rcode[i]; for(i=0; i<sizeof(total->opcode)/sizeof(stc_type); i++) @@ -324,7 +329,8 @@ read_child_stats(struct nsd* nsd, struct nsd_child* child, int fd) "%d: %s", (int)child->pid, strerror(errno)); } else { stats_add(&nsd->st, &s); - child->query_count = s.qudp + s.qudp6 + s.ctcp + s.ctcp6; + child->query_count = s.qudp + s.qudp6 + s.ctcp + s.ctcp6 + + s.ctls + s.ctls6; /* we know that the child is going to close the connection * now (this is an ACK of the QUIT_W_STATS so we know the * child is done, no longer sending e.g. NOTIFY contents) */ @@ -593,6 +599,7 @@ ipc_xfrd_set_listening(struct xfrd_state* xfrd, short mode) int fd = xfrd->ipc_handler.ev_fd; struct event_base* base = xfrd->event_base; event_del(&xfrd->ipc_handler); + memset(&xfrd->ipc_handler, 0, sizeof(xfrd->ipc_handler)); event_set(&xfrd->ipc_handler, fd, mode, xfrd_handle_ipc, xfrd); if(event_base_set(base, &xfrd->ipc_handler) != 0) log_msg(LOG_ERR, "ipc: cannot set event_base"); diff --git a/usr.sbin/nsd/mini_event.c b/usr.sbin/nsd/mini_event.c index a39d682cc51..2478a6dcd1a 100644 --- a/usr.sbin/nsd/mini_event.c +++ b/usr.sbin/nsd/mini_event.c @@ -304,8 +304,7 @@ event_base_free(struct event_base* base) { if(!base) return; - if(base->times) - free(base->times); + /* base->times is allocated in region and is freed with the region */ if(base->fds) free(base->fds); if(base->signals) @@ -362,7 +361,7 @@ event_add(struct event* ev, struct timeval* tv) struct timeval* now = ev->ev_base->time_tv; ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec; ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec; - while(ev->ev_timeout.tv_usec > 1000000) { + while(ev->ev_timeout.tv_usec >= 1000000) { ev->ev_timeout.tv_usec -= 1000000; ev->ev_timeout.tv_sec++; } diff --git a/usr.sbin/nsd/nsd-checkconf.8.in b/usr.sbin/nsd/nsd-checkconf.8.in index 18d669750e5..9afe139d3c8 100644 --- a/usr.sbin/nsd/nsd-checkconf.8.in +++ b/usr.sbin/nsd/nsd-checkconf.8.in @@ -1,4 +1,4 @@ -.TH "nsd\-checkconf" "8" "Mar 25, 2019" "NLnet Labs" "nsd 4.1.27" +.TH "nsd\-checkconf" "8" "Aug 19, 2019" "NLnet Labs" "nsd 4.2.2" .\" 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 20eb42a8cf1..424796dac99 100644 --- a/usr.sbin/nsd/nsd-checkconf.c +++ b/usr.sbin/nsd/nsd-checkconf.c @@ -366,11 +366,13 @@ config_print_zone(nsd_options_type* opt, const char* k, int s, const char *o, SERV_GET_BIN(do_ip6, o); SERV_GET_BIN(reuseport, o); SERV_GET_BIN(hide_version, o); + SERV_GET_BIN(hide_identity, o); SERV_GET_BIN(zonefiles_check, o); SERV_GET_BIN(log_time_ascii, o); SERV_GET_BIN(round_robin, o); SERV_GET_BIN(minimal_responses, o); SERV_GET_BIN(refuse_any, o); + SERV_GET_BIN(tcp_reject_overflow, o); /* str */ SERV_GET_PATH(final, database, o); SERV_GET_STR(identity, o); @@ -385,6 +387,10 @@ config_print_zone(nsd_options_type* opt, const char* k, int s, const char *o, SERV_GET_PATH(final, xfrdir, o); SERV_GET_PATH(final, zonelistfile, o); SERV_GET_STR(port, o); + SERV_GET_STR(tls_service_key, o); + SERV_GET_STR(tls_service_ocsp, o); + SERV_GET_STR(tls_service_pem, o); + SERV_GET_STR(tls_port, o); /* int */ SERV_GET_INT(server_count, o); SERV_GET_INT(tcp_count, o); @@ -397,6 +403,8 @@ config_print_zone(nsd_options_type* opt, const char* k, int s, const char *o, SERV_GET_INT(statistics, o); SERV_GET_INT(xfrd_reload_timeout, o); SERV_GET_INT(verbosity, o); + SERV_GET_INT(send_buffer_size, o); + SERV_GET_INT(receive_buffer_size, o); #ifdef RATELIMIT SERV_GET_INT(rrl_size, o); SERV_GET_INT(rrl_ratelimit, o); @@ -493,7 +501,12 @@ config_test_print_server(nsd_options_type* opt) printf("\treuseport: %s\n", opt->reuseport?"yes":"no"); printf("\tdo-ip4: %s\n", opt->do_ip4?"yes":"no"); printf("\tdo-ip6: %s\n", opt->do_ip6?"yes":"no"); + printf("\tsend-buffer-size: %d\n", opt->send_buffer_size); + printf("\treceive-buffer-size: %d\n", opt->receive_buffer_size); printf("\thide-version: %s\n", opt->hide_version?"yes":"no"); + printf("\thide-identity: %s\n", opt->hide_identity?"yes":"no"); + printf("\ttcp-reject-overflow: %s\n", + opt->tcp_reject_overflow ? "yes" : "no"); print_string_var("database:", opt->database); print_string_var("identity:", opt->identity); print_string_var("version:", opt->version); @@ -536,6 +549,10 @@ config_test_print_server(nsd_options_type* opt) #endif printf("\tzonefiles-check: %s\n", opt->zonefiles_check?"yes":"no"); printf("\tzonefiles-write: %d\n", opt->zonefiles_write); + print_string_var("tls-service-key:", opt->tls_service_key); + print_string_var("tls-service-pem:", opt->tls_service_pem); + print_string_var("tls-service-ocsp:", opt->tls_service_ocsp); + print_string_var("tls-port:", opt->tls_port); #ifdef USE_DNSTAP printf("\ndnstap:\n"); @@ -581,7 +598,6 @@ config_test_print_server(nsd_options_type* opt) print_string_var("name:", zone->name); print_zone_content_elems(zone->pattern); } - } static int @@ -720,7 +736,7 @@ main(int argc, char* argv[]) log_init("nsd-checkconf"); /* Parse the command line... */ - while ((c = getopt(argc, argv, "vfo:a:p:s:z:")) != -1) { + while ((c = getopt(argc, argv, "vfho:a:p:s:z:")) != -1) { switch (c) { case 'v': verbose = 1; @@ -753,6 +769,7 @@ main(int argc, char* argv[]) case 'z': conf_zone = optarg; break; + case 'h': default: usage(); }; diff --git a/usr.sbin/nsd/nsd-checkzone.8.in b/usr.sbin/nsd/nsd-checkzone.8.in index b6e2499b1a6..7ade47b2af5 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" "Mar 25, 2019" "NLnet Labs" "nsd 4.1.27" +.TH "nsd\-checkzone" "8" "Aug 19, 2019" "NLnet Labs" "nsd 4.2.2" .\" Copyright (c) 2014, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" diff --git a/usr.sbin/nsd/nsd-checkzone.c b/usr.sbin/nsd/nsd-checkzone.c index 21b8ce5648d..9bf74fd6538 100644 --- a/usr.sbin/nsd/nsd-checkzone.c +++ b/usr.sbin/nsd/nsd-checkzone.c @@ -61,6 +61,10 @@ check_zone(struct nsd* nsd, const char* name, const char* fname) errors = zonec_read(name, fname, zone); if(errors > 0) { printf("zone %s file %s has %u errors\n", name, fname, errors); +#ifdef MEMCLEAN /* otherwise, the OS collects memory pages */ + namedb_close(nsd->db); + region_destroy(nsd->options->region); +#endif exit(1); } printf("zone %s is ok\n", name); diff --git a/usr.sbin/nsd/nsd-control.8.in b/usr.sbin/nsd/nsd-control.8.in index b6fe680b89b..19e37355fc1 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" "Mar 25, 2019" "NLnet Labs" "nsd 4.1.27" +.TH "nsd\-control" "8" "Aug 19, 2019" "NLnet Labs" "nsd 4.2.2" .\" Copyright (c) 2011, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" @@ -192,7 +192,7 @@ After running the script as root, turn on \fBcontrol\-enable\fR in The \fIstats\fR command shows a number of statistic counters. .TP .I num.queries -number of queries received (the tcp and udp queries added up). +number of queries received (the tls, tcp and udp queries added up). .TP .I serverX.queries number of queries handled by the server process. The number of @@ -253,6 +253,12 @@ number of connections over TCP ip4. .I num.tcp6 number of connections over TCP ip6. .TP +.I num.tls +number of connections over TLS ip4. TLS queries are not part of num.tcp. +.TP +.I num.tls6 +number of connections over TLS ip6. TLS queries are not part of num.tcp6. +.TP .I num.answer_wo_aa number of answers with NOERROR rcode and without AA flag, this includes the referrals. .TP diff --git a/usr.sbin/nsd/nsd-control.c b/usr.sbin/nsd/nsd-control.c index efac9ac530d..d6c7744d85e 100644 --- a/usr.sbin/nsd/nsd-control.c +++ b/usr.sbin/nsd/nsd-control.c @@ -42,8 +42,8 @@ */ #include "config.h" +#include <stdio.h> #ifdef HAVE_SSL - #include <sys/types.h> #include <unistd.h> #include <string.h> @@ -163,6 +163,12 @@ setup_ctx(struct nsd_options* cfg) if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3) != SSL_OP_NO_SSLv3) ssl_err("could not set SSL_OP_NO_SSLv3"); +#if defined(SSL_OP_NO_RENEGOTIATION) + /* disable client renegotiation */ + if((SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION) & + SSL_OP_NO_RENEGOTIATION) != SSL_OP_NO_RENEGOTIATION) + ssl_err("could not set SSL_OP_NO_RENEGOTIATION"); +#endif if(!SSL_CTX_use_certificate_file(ctx,c_cert,SSL_FILETYPE_PEM)) ssl_path_err("Error setting up SSL_CTX client cert", c_cert); if(!SSL_CTX_use_PrivateKey_file(ctx,c_key,SSL_FILETYPE_PEM)) diff --git a/usr.sbin/nsd/nsd.8.in b/usr.sbin/nsd/nsd.8.in index 1d58c5ad877..ca9ae0218a2 100644 --- a/usr.sbin/nsd/nsd.8.in +++ b/usr.sbin/nsd/nsd.8.in @@ -1,9 +1,9 @@ -.TH "NSD" "8" "Mar 25, 2019" "NLnet Labs" "NSD 4.1.27" +.TH "NSD" "8" "Aug 19, 2019" "NLnet Labs" "NSD 4.2.2" .\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" .B nsd -\- Name Server Daemon (NSD) version 4.1.27. +\- Name Server Daemon (NSD) version 4.2.2. .SH "SYNOPSIS" .B nsd .RB [ \-4 ] diff --git a/usr.sbin/nsd/nsd.c b/usr.sbin/nsd/nsd.c index c773fc99d34..9434bff7c9b 100644 --- a/usr.sbin/nsd/nsd.c +++ b/usr.sbin/nsd/nsd.c @@ -661,6 +661,9 @@ main(int argc, char *argv[]) nsd.outgoing_tcp_mss = nsd.options->outgoing_tcp_mss; nsd.ipv4_edns_size = nsd.options->ipv4_edns_size; nsd.ipv6_edns_size = nsd.options->ipv6_edns_size; +#ifdef HAVE_SSL + nsd.tls_ctx = NULL; +#endif if(udp_port == 0) { @@ -942,11 +945,20 @@ main(int argc, char *argv[]) "not be started", argv0); } #if defined(HAVE_SSL) + if(nsd.options->control_enable || (nsd.options->tls_service_key && nsd.options->tls_service_key[0])) { + perform_openssl_init(); + } if(nsd.options->control_enable) { /* read ssl keys while superuser and outside chroot */ if(!(nsd.rc = daemon_remote_create(nsd.options))) error("could not perform remote control setup"); } + if(nsd.options->tls_service_key && nsd.options->tls_service_key[0] + && nsd.options->tls_service_pem && nsd.options->tls_service_pem[0]) { + if(!(nsd.tls_ctx = server_tls_ctx_create(&nsd, NULL, + nsd.options->tls_service_ocsp))) + error("could not set up tls SSL_CTX"); + } #endif /* HAVE_SSL */ /* Unless we're debugging, fork... */ diff --git a/usr.sbin/nsd/nsd.conf.5.in b/usr.sbin/nsd/nsd.conf.5.in index a9c10213149..444c3229c94 100644 --- a/usr.sbin/nsd/nsd.conf.5.in +++ b/usr.sbin/nsd/nsd.conf.5.in @@ -1,4 +1,4 @@ -.TH "nsd.conf" "5" "Mar 25, 2019" "NLnet Labs" "nsd 4.1.27" +.TH "nsd.conf" "5" "Aug 19, 2019" "NLnet Labs" "nsd 4.2.2" .\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved. .\" See LICENSE for the license. .SH "NAME" @@ -149,7 +149,7 @@ clause. There may only be one clause. .TP .B ip\-address:\fR <ip4 or ip6>[@port] -NSD will bind to the listed ip\-address. Can be give multiple times +NSD will bind to the listed ip\-address. Can be given multiple times to bind multiple ip\-addresses. Optionally, a port number can be given. If none are given NSD listens to the wildcard interface. Same as commandline option .BR \-a. @@ -181,6 +181,12 @@ than 1 (such as, equal to the number of cpus). The default is no. It works on Linux, but does not work on FreeBSD, and likely does not work on other systems. .TP +.B send\-buffer\-size:\fR <number> +Set the send buffer size for query-servicing sockets. Set to 0 to use the default settings. +.TP +.B receive\-buffer\-size:\fR <number> +Set the receive buffer size for query-servicing sockets. Set to 0 to use the default settings. +.TP .B debug\-mode:\fR <yes or no> Turns on debugging mode for nsd, does not fork a daemon process. Default is no. Same as commandline option @@ -217,6 +223,7 @@ Returns the specified identity when asked for CH TXT ID.SERVER. Default is the name as returned by gethostname(3). Same as commandline option .BR \-i . +See hide\-identity to set the server to not respond to such queries. .TP .B version:\fR <string> Returns the specified version string when asked for CH TXT version.server, @@ -244,6 +251,10 @@ The maximum number of concurrent, active TCP connections by each server. Default is 100. Same as commandline option .BR \-n . .TP +.B tcp\-reject\-overflow:\fR <yes or no> +If set to yes, TCP connections made beyond the maximum set by tcp-count will +be dropped immediately (accepted and closed). Default is no. +.TP .B tcp\-query\-count:\fR <number> The maximum number of queries served on a single TCP connection. Default is 0, meaning there is no maximum. @@ -359,6 +370,10 @@ And notify refusal, and axfr request refusals. Prevent NSD from replying with the version string on CHAOS class queries. Default is no. .TP +.B hide\-identity:\fR <yes or no> +Prevent NSD from replying with the identity string on CHAOS class +queries. Default is no. +.TP .B log\-time\-ascii:\fR <yes or no> Log time in ascii, if "no" then in seconds epoch. Default is yes. This chooses the format when logging to file. The printout via syslog @@ -436,6 +451,38 @@ whitelisted. Default @ratelimit_default@ (with a suggested 2000 qps). With the r specific queries to receive this qps limit instead of the normal limit. With the value 0 the rate is unlimited. .\" rrlend +.TP +.B tls\-service\-key:\fR <filename> +If enabled, the server provides TLS service on TCP sockets with the TLS +service port number. The port number (853) is configured with tls\-port. +To turn it on, create an interface: option line in config with @port +appended to the IP-address. This creates the extra socket on which the +DNS over TLS service is provided. +.IP +The file is the private key for the TLS session. The public certificate is +in the tls-service-pem file. Default is "", turned off. Requires a +restart (a reload is not enough) if changed, because the private key is +read while root permissions are held and before chroot (if any). +.TP +.B tls\-service\-pem:\fR <filename> +The public key certificate pem file for the tls service. Default is "", turned off. +.TP +.B tls\-service\-ocsp:\fR <filename> +The ocsp pem file for the tls service, for OCSP stapling. Default is "", +turned off. An external process prepares and updates the OCSP stapling data. +Like this, +.RS 9 +openssl ocsp -no_nonce \\ + -respout /path/to/ocsp.pem \\ + -CAfile /path/to/ca_and_any_intermediate.pem \\ + -issuer /path/to/direct_issuer.pem \\ + -cert /path/to/cert.pem \\ + -url "$( openssl x509 -noout -text -in /path/to/cert.pem | grep 'OCSP - URI:' | cut -d: -f2,3 )" +.RE +.TP +.B tls\-port:\fR <number> +The port number on which to provide TCP TLS service, default is 853, only +interfaces configured with that port number as @number get DNS over TLS service. .SS "Remote Control" The .B remote\-control: diff --git a/usr.sbin/nsd/nsd.conf.sample.in b/usr.sbin/nsd/nsd.conf.sample.in index bd3a6e89cab..32dea906e9b 100644 --- a/usr.sbin/nsd/nsd.conf.sample.in +++ b/usr.sbin/nsd/nsd.conf.sample.in @@ -33,6 +33,14 @@ server: # use the reuseport socket option for performance. Default no. # reuseport: no + # override maximum socket send buffer size. Default of 0 results in + # send buffer size being set to 1048576 (bytes). + # send-buffer-size: 1048576 + + # override maximum socket receive buffer size. Default of 0 results in + # receive buffer size being set to 1048576 (bytes). + # receive-buffer-size: 1048576 + # enable debug mode, does not fork daemon process into the background. # debug-mode: no @@ -85,6 +93,9 @@ server: # don't answer VERSION.BIND and VERSION.SERVER CHAOS class queries # hide-version: no + # don't answer HOSTNAME.BIND and ID.SERVER CHAOS class queries + # hide-identity: no + # version string the server responds with for chaos queries. # default is 'NSD x.y.z' with the server's version number. # version: "NSD" @@ -98,6 +109,11 @@ server: # Maximum number of concurrent TCP connections per server. # tcp-count: 100 + # Accept (and immediately close) TCP connections after maximum number + # of connections is reached to prevent kernel connection queue from + # growing. + # tcp-reject-overflow: no + # Maximum number of queries served on a single TCP connection. # By default 0, which means no maximum. # tcp-query-count: 0 @@ -189,6 +205,14 @@ 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 c900ca6cbaa..de3ae8e1d43 100644 --- a/usr.sbin/nsd/nsd.h +++ b/usr.sbin/nsd/nsd.h @@ -11,6 +11,9 @@ #define _NSD_H_ #include <signal.h> +#ifdef HAVE_OPENSSL_SSL_H +#include <openssl/ssl.h> +#endif #include "dns.h" #include "edns.h" @@ -244,6 +247,7 @@ struct nsd stc_type qclass[4]; /* Class IN or Class CH or other */ stc_type qudp, qudp6; /* Number of queries udp and udp6 */ stc_type ctcp, ctcp6; /* Number of tcp and tcp6 connections */ + stc_type ctls, ctls6; /* Number of tls and tls6 connections */ stc_type rcode[17], opcode[6]; /* Rcodes & opcodes */ /* Dropped, truncated, queries for nonconfigured zone, tx errors */ stc_type dropped, truncated, wrongzone, txerr, rxerr; @@ -276,6 +280,11 @@ struct nsd unsigned int err_limit_count; struct nsd_options* options; + +#ifdef HAVE_SSL + /* TLS specific configuration */ + SSL_CTX *tls_ctx; +#endif }; extern struct nsd nsd; @@ -295,6 +304,7 @@ void server_child(struct nsd *nsd); void server_shutdown(struct nsd *nsd) ATTR_NORETURN; void server_close_all_sockets(struct nsd_socket sockets[], size_t n); struct event_base* nsd_child_event_base(void); +void service_remaining_tcp(struct nsd* nsd); /* extra domain numbers for temporary domains */ #define EXTRA_DOMAIN_NUMBERS 1024 #define SLOW_ACCEPT_TIMEOUT 2 /* in seconds */ @@ -311,6 +321,11 @@ void server_prepare_xfrd(struct nsd *nsd); void server_start_xfrd(struct nsd *nsd, int del_db, int reload_active); /* send SOA serial numbers to xfrd */ void server_send_soa_xfrd(struct nsd *nsd, int shortsoa); +#ifdef HAVE_SSL +SSL_CTX* server_tls_ctx_setup(char* key, char* pem, char* verifypem); +SSL_CTX* server_tls_ctx_create(struct nsd *nsd, char* verifypem, char* ocspfile); +void perform_openssl_init(void); +#endif ssize_t block_read(struct nsd* nsd, int s, void* p, ssize_t sz, int timeout); #endif /* _NSD_H_ */ diff --git a/usr.sbin/nsd/options.c b/usr.sbin/nsd/options.c index d28a2c45dbb..81b80c01f30 100644 --- a/usr.sbin/nsd/options.c +++ b/usr.sbin/nsd/options.c @@ -52,9 +52,12 @@ nsd_options_create(region_type* region) opt->ip_addresses = NULL; opt->ip_transparent = 0; opt->ip_freebind = 0; + opt->send_buffer_size = 0; + opt->receive_buffer_size = 0; opt->debug_mode = 0; opt->verbosity = 0; opt->hide_version = 0; + opt->hide_identity = 0; opt->do_ip4 = 1; opt->do_ip6 = 1; opt->database = DBFILE; @@ -68,6 +71,7 @@ nsd_options_create(region_type* region) opt->refuse_any = 1; opt->server_count = 1; opt->tcp_count = 100; + opt->tcp_reject_overflow = 0; opt->tcp_query_count = 0; opt->tcp_timeout = TCP_TIMEOUT; opt->tcp_mss = 0; @@ -113,6 +117,10 @@ nsd_options_create(region_type* region) opt->zonefiles_write = ZONEFILES_WRITE_INTERVAL; else opt->zonefiles_write = 0; opt->xfrd_reload_timeout = 1; + opt->tls_service_key = NULL; + opt->tls_service_ocsp = NULL; + opt->tls_service_pem = NULL; + opt->tls_port = TLS_PORT; opt->control_enable = 0; opt->control_interface = NULL; opt->control_port = NSD_CONTROL_PORT; diff --git a/usr.sbin/nsd/options.h b/usr.sbin/nsd/options.h index 1d30156bbbe..97e2dd07ed8 100644 --- a/usr.sbin/nsd/options.h +++ b/usr.sbin/nsd/options.h @@ -61,9 +61,12 @@ struct nsd_options { int ip_transparent; int ip_freebind; + int send_buffer_size; + int receive_buffer_size; int debug_mode; int verbosity; int hide_version; + int hide_identity; int do_ip4; int do_ip6; const char* database; @@ -72,6 +75,7 @@ struct nsd_options { const char* logfile; int server_count; int tcp_count; + int tcp_reject_overflow; int tcp_query_count; int tcp_timeout; int tcp_mss; @@ -97,6 +101,15 @@ struct nsd_options { int refuse_any; int reuseport; + /* private key file for TLS */ + char* tls_service_key; + /* ocsp stapling file for TLS */ + char* tls_service_ocsp; + /* certificate file for TLS */ + char* tls_service_pem; + /* TLS dedicated port */ + const char* tls_port; + /** remote control section. enable toggle. */ int control_enable; /** the interfaces the remote control should listen on */ diff --git a/usr.sbin/nsd/query.c b/usr.sbin/nsd/query.c index e3b5fe6e9c8..8d42a27444b 100644 --- a/usr.sbin/nsd/query.c +++ b/usr.sbin/nsd/query.c @@ -530,13 +530,17 @@ answer_chaos(struct nsd *nsd, query_type *q) (q->qname->name_size == 15 && memcmp(dname_name(q->qname), "\010hostname\004bind", 15) == 0)) { - /* Add ID */ - query_addtxt(q, + if(!nsd->options->hide_identity) { + /* Add ID */ + query_addtxt(q, buffer_begin(q->packet) + QHEADERSZ, CLASS_CH, 0, nsd->identity); - ANCOUNT_SET(q->packet, ANCOUNT(q->packet) + 1); + ANCOUNT_SET(q->packet, ANCOUNT(q->packet) + 1); + } else { + RCODE_SET(q->packet, RCODE_REFUSE); + } } else if ((q->qname->name_size == 16 && memcmp(dname_name(q->qname), "\007version\006server", 16) == 0) || (q->qname->name_size == 14 diff --git a/usr.sbin/nsd/remote.c b/usr.sbin/nsd/remote.c index 147535f1a23..80e50053822 100644 --- a/usr.sbin/nsd/remote.c +++ b/usr.sbin/nsd/remote.c @@ -252,48 +252,13 @@ timeval_subtract(struct timeval* d, const struct timeval* end, static int remote_setup_ctx(struct daemon_remote* rc, struct nsd_options* cfg) { - char* s_cert; - char* s_key; - rc->ctx = SSL_CTX_new(SSLv23_server_method()); + char* s_cert = cfg->server_cert_file; + char* s_key = cfg->server_key_file; + rc->ctx = server_tls_ctx_setup(s_key, s_cert, s_cert); if(!rc->ctx) { - log_crypto_err("could not SSL_CTX_new"); + log_msg(LOG_ERR, "could not setup remote control TLS context"); return 0; } - /* no SSLv2, SSLv3 because has defects */ - if((SSL_CTX_set_options(rc->ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2) - != SSL_OP_NO_SSLv2){ - log_crypto_err("could not set SSL_OP_NO_SSLv2"); - return 0; - } - if((SSL_CTX_set_options(rc->ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3) - != SSL_OP_NO_SSLv3){ - log_crypto_err("could not set SSL_OP_NO_SSLv3"); - return 0; - } - s_cert = cfg->server_cert_file; - s_key = cfg->server_key_file; - VERBOSITY(2, (LOG_INFO, "setup SSL certificates")); - if (!SSL_CTX_use_certificate_file(rc->ctx,s_cert,SSL_FILETYPE_PEM)) { - log_msg(LOG_ERR, "Error for server-cert-file: %s", s_cert); - log_crypto_err("Error in SSL_CTX use_certificate_file"); - return 0; - } - if(!SSL_CTX_use_PrivateKey_file(rc->ctx,s_key,SSL_FILETYPE_PEM)) { - log_msg(LOG_ERR, "Error for server-key-file: %s", s_key); - log_crypto_err("Error in SSL_CTX use_PrivateKey_file"); - return 0; - } - if(!SSL_CTX_check_private_key(rc->ctx)) { - log_msg(LOG_ERR, "Error for server-key-file: %s", s_key); - log_crypto_err("Error in SSL_CTX check_private_key"); - return 0; - } - if(!SSL_CTX_load_verify_locations(rc->ctx, s_cert, NULL)) { - log_crypto_err("Error setting up SSL_CTX verify locations"); - return 0; - } - SSL_CTX_set_client_CA_list(rc->ctx, SSL_load_client_CA_file(s_cert)); - SSL_CTX_set_verify(rc->ctx, SSL_VERIFY_PEER, NULL); return 1; } @@ -305,38 +270,6 @@ daemon_remote_create(struct nsd_options* cfg) rc->max_active = 10; assert(cfg->control_enable); - /* init SSL library */ -#ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS - ERR_load_crypto_strings(); -#endif - ERR_load_SSL_strings(); -#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO) - OpenSSL_add_all_algorithms(); -#else - OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS - | OPENSSL_INIT_ADD_ALL_DIGESTS - | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); -#endif -#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) - (void)SSL_library_init(); -#else - OPENSSL_init_ssl(0, NULL); -#endif - - if(!RAND_status()) { - /* try to seed it */ - unsigned char buf[256]; - unsigned int v, seed=(unsigned)time(NULL) ^ (unsigned)getpid(); - size_t i; - v = seed; - for(i=0; i<256/sizeof(v); i++) { - memmove(buf+i*sizeof(v), &v, sizeof(v)); - v = v*seed + (unsigned int)i; - } - RAND_seed(buf, 256); - log_msg(LOG_WARNING, "warning: no entropy, seeding openssl PRNG with time"); - } - if(options_remote_is_address(cfg)) { if(!remote_setup_ctx(rc, cfg)) { daemon_remote_delete(rc); @@ -593,6 +526,7 @@ daemon_remote_attach(struct daemon_remote* rc, struct xfrd_state* xfrd) for(p = rc->accept_list; p; p = p->next) { /* add event */ fd = p->c.ev_fd; + memset(&p->c, 0, sizeof(p->c)); event_set(&p->c, fd, EV_PERSIST|EV_READ, remote_accept_callback, p); if(event_base_set(xfrd->event_base, &p->c) != 0) @@ -670,6 +604,7 @@ remote_accept_callback(int fd, short event, void* arg) n->tval.tv_usec = 0L; n->fd = newfd; + memset(&n->c, 0, sizeof(n->c)); event_set(&n->c, newfd, EV_PERSIST|EV_TIMEOUT|EV_READ, remote_control_callback, n); if(event_base_set(xfrd->event_base, &n->c) != 0) { @@ -2372,6 +2307,7 @@ remote_handshake_later(struct daemon_remote* rc, struct rc_state* s, int fd, } s->shake_state = rc_hs_read; event_del(&s->c); + memset(&s->c, 0, sizeof(s->c)); event_set(&s->c, fd, EV_PERSIST|EV_TIMEOUT|EV_READ, remote_control_callback, s); if(event_base_set(xfrd->event_base, &s->c) != 0) @@ -2386,6 +2322,7 @@ remote_handshake_later(struct daemon_remote* rc, struct rc_state* s, int fd, } s->shake_state = rc_hs_write; event_del(&s->c); + memset(&s->c, 0, sizeof(s->c)); event_set(&s->c, fd, EV_PERSIST|EV_TIMEOUT|EV_WRITE, remote_control_callback, s); if(event_base_set(xfrd->event_base, &s->c) != 0) @@ -2553,6 +2490,12 @@ print_stat_block(RES* ssl, char* n, char* d, struct nsdst* st) /* ctcp6 */ if(!ssl_printf(ssl, "%s%snum.tcp6=%lu\n", n, d, (unsigned long)st->ctcp6)) return; + /* ctls */ + if(!ssl_printf(ssl, "%s%snum.tls=%lu\n", n, d, (unsigned long)st->ctls)) + return; + /* ctls6 */ + if(!ssl_printf(ssl, "%s%snum.tls6=%lu\n", n, d, (unsigned long)st->ctls6)) + return; /* nona */ if(!ssl_printf(ssl, "%s%snum.answer_wo_aa=%lu\n", n, d, @@ -2640,7 +2583,7 @@ zonestat_print(RES* ssl, xfrd_state_type* xfrd, int clear) /* stat0 contains the details that we want to print */ if(!ssl_printf(ssl, "%s%snum.queries=%lu\n", name, ".", (unsigned long)(stat0.qudp + stat0.qudp6 + stat0.ctcp + - stat0.ctcp6))) + stat0.ctcp6 + stat0.ctls + stat0.ctls6))) return; print_stat_block(ssl, name, ".", &stat0); } diff --git a/usr.sbin/nsd/server.c b/usr.sbin/nsd/server.c index ff2f62fff82..c78f3b51266 100644 --- a/usr.sbin/nsd/server.c +++ b/usr.sbin/nsd/server.c @@ -16,6 +16,9 @@ #include <sys/wait.h> #include <netinet/in.h> +#ifdef USE_TCP_FASTOPEN + #include <netinet/tcp.h> +#endif #include <arpa/inet.h> #include <assert.h> @@ -40,6 +43,15 @@ #ifdef HAVE_OPENSSL_RAND_H #include <openssl/rand.h> #endif +#ifdef HAVE_OPENSSL_SSL_H +#include <openssl/ssl.h> +#endif +#ifdef HAVE_OPENSSL_ERR_H +#include <openssl/err.h> +#endif +#ifdef HAVE_OPENSSL_OCSP_H +#include <openssl/ocsp.h> +#endif #ifndef USE_MINI_EVENT # ifdef HAVE_EVENT_H # include <event.h> @@ -71,6 +83,11 @@ #define RELOAD_SYNC_TIMEOUT 25 /* seconds */ +#ifdef USE_TCP_FASTOPEN + #define TCP_FASTOPEN_FILE "/proc/sys/net/ipv4/tcp_fastopen" + #define TCP_FASTOPEN_SERVER_BIT_MASK 0x2 +#endif + /* * Data for the UDP handlers. */ @@ -86,6 +103,10 @@ struct tcp_accept_handler_data { struct nsd_socket *socket; int event_added; struct event event; +#ifdef HAVE_SSL + /* handler accepts TLS connections on the dedicated port */ + int tls_accept; +#endif }; /* @@ -99,6 +120,11 @@ static struct tcp_accept_handler_data* tcp_accept_handlers; static struct event slowaccept_event; static int slowaccept; +#ifdef HAVE_SSL +static unsigned char *ocspdata = NULL; +static long ocspdata_len = 0; +#endif + #ifndef NONBLOCKING_IS_BROKEN # define NUM_RECV_PER_SELECT 100 #endif @@ -170,7 +196,23 @@ struct tcp_handler_data * The timeout in msec for this tcp connection */ int tcp_timeout; +#ifdef HAVE_SSL + /* + * TLS object. + */ + SSL* tls; + + /* + * TLS handshake state. + */ + enum { tls_hs_none, tls_hs_read, tls_hs_write, + tls_hs_read_event, tls_hs_write_event } shake_state; +#endif + /* list of connections, for service of remaining tcp channels */ + struct tcp_handler_data *prev, *next; }; +/* global that is the list of active tcp channels */ +static struct tcp_handler_data *tcp_active_list = NULL; /* * Handle incoming queries on the UDP server sockets. @@ -202,6 +244,29 @@ static void handle_tcp_reading(int fd, short event, void* arg); */ static void handle_tcp_writing(int fd, short event, void* arg); +#ifdef HAVE_SSL +/* Create SSL object and associate fd */ +static SSL* incoming_ssl_fd(SSL_CTX* ctx, int fd); +/* + * Handle TLS handshake. May be called multiple times if incomplete. + */ +static int tls_handshake(struct tcp_handler_data* data, int fd, int writing); + +/* + * Handle incoming queries on a TLS over TCP connection. The TLS + * connections are configured to be non-blocking and the handler may + * be called multiple times before a complete query is received. + */ +static void handle_tls_reading(int fd, short event, void* arg); + +/* + * Handle outgoing responses on a TLS over TCP connection. The TLS + * connections are configured to be non-blocking and the handler may + * be called multiple times before a complete response is sent. + */ +static void handle_tls_writing(int fd, short event, void* arg); +#endif + /* * Send all children the quit nonblocking, then close pipe. */ @@ -224,6 +289,34 @@ static uint32_t compression_table_capacity = 0; static uint32_t compression_table_size = 0; static domain_type* compressed_dnames[MAXRRSPP]; +#ifdef USE_TCP_FASTOPEN +/* Checks to see if the kernel value must be manually changed in order for + TCP Fast Open to support server mode */ +static void report_tcp_fastopen_config() { + + int tcp_fastopen_fp; + uint8_t tcp_fastopen_value; + + if ( (tcp_fastopen_fp = open(TCP_FASTOPEN_FILE, O_RDONLY)) == -1 ) { + log_msg(LOG_INFO,"Error opening " TCP_FASTOPEN_FILE ": %s\n", strerror(errno)); + } + if (read(tcp_fastopen_fp, &tcp_fastopen_value, 1) == -1 ) { + log_msg(LOG_INFO,"Error reading " TCP_FASTOPEN_FILE ": %s\n", strerror(errno)); + close(tcp_fastopen_fp); + } + if (!(tcp_fastopen_value & TCP_FASTOPEN_SERVER_BIT_MASK)) { + log_msg(LOG_WARNING, "Error: TCP Fast Open support is available and configured in NSD by default.\n"); + log_msg(LOG_WARNING, "However the kernel paramenters are not configured to support TCP_FASTOPEN in server mode.\n"); + log_msg(LOG_WARNING, "To enable TFO use the command:"); + log_msg(LOG_WARNING, " 'sudo sysctl -w net.ipv4.tcp_fastopen=2' for pure server mode or\n"); + log_msg(LOG_WARNING, " 'sudo sysctl -w net.ipv4.tcp_fastopen=3' for both client and server mode\n"); + log_msg(LOG_WARNING, "NSD will not have TCP Fast Open available until this change is made.\n"); + close(tcp_fastopen_fp); + } + close(tcp_fastopen_fp); +} +#endif + /* * Remove the specified pid from the list of child pids. Returns -1 if * the pid is not in the list, child_num otherwise. The field is set to 0. @@ -577,6 +670,9 @@ server_init_ifs(struct nsd *nsd, size_t from, size_t to, int* reuseport_works) #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)) int on = 1; #endif +#ifdef USE_TCP_FASTOPEN + int qlen; +#endif /* UDP */ @@ -638,60 +734,62 @@ server_init_ifs(struct nsd *nsd, size_t from, size_t to, int* reuseport_works) #else (void)reuseport_works; #endif /* SO_REUSEPORT */ -#if defined(SO_RCVBUF) || defined(SO_SNDBUF) - if(1) { - int rcv = 1*1024*1024; - int snd = 1*1024*1024; - -#ifdef SO_RCVBUF +#if defined(SO_RCVBUF) + { + 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, " + 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; - } + return -1; + } + } # else - if(1) { -# endif /* SO_RCVBUFFORCE */ if(setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_RCVBUF, (void*)&rcv, - (socklen_t)sizeof(rcv)) < 0) { + (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 */ } #endif /* SO_RCVBUF */ #ifdef SO_SNDBUF + { + 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, " + 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; - } + return -1; + } + } # else - if(1) { -# endif /* SO_SNDBUFFORCE */ if(setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_SNDBUF, (void*)&snd, - (socklen_t)sizeof(snd)) < 0) { + (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 */ } #endif /* SO_SNDBUF */ - } -#endif /* defined(SO_RCVBUF) || defined(SO_SNDBUF) */ - #if defined(INET6) if (addr->ai_family == AF_INET6) { # if defined(IPV6_V6ONLY) @@ -837,13 +935,19 @@ server_init_ifs(struct nsd *nsd, size_t from, size_t to, int* reuseport_works) if ( bind(nsd->udp[i].s, (struct sockaddr *) addr->ai_addr, addr->ai_addrlen) != 0) { - log_msg(LOG_ERR, "can't bind udp socket: %s", strerror(errno)); + 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; } } /* TCP */ +#ifdef USE_TCP_FASTOPEN + report_tcp_fastopen_config(); +#endif + /* Make a socket... */ for (i = from; i < to; i++) { /* for reuseports copy socket specs of first entries */ @@ -974,10 +1078,40 @@ server_init_ifs(struct nsd *nsd, size_t from, size_t to, int* reuseport_works) if( bind(nsd->tcp[i].s, (struct sockaddr *) addr->ai_addr, addr->ai_addrlen) != 0) { - log_msg(LOG_ERR, "can't bind tcp socket: %s", strerror(errno)); + 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; } +#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; +#else + /* 5 is recommended on linux */ + qlen = 5; +#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) { +#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 + } +#endif + } +#endif + /* Listen to it... */ if (listen(nsd->tcp[i].s, TCP_BACKLOG) == -1) { log_msg(LOG_ERR, "can't listen: %s", strerror(errno)); @@ -1039,9 +1173,12 @@ server_prepare(struct nsd *nsd) #else uint32_t v = getpid() ^ time(NULL); srandom((unsigned long)v); +# ifdef HAVE_SSL if(RAND_status() && RAND_bytes((unsigned char*)&v, sizeof(v)) > 0) hash_set_raninit(v); - else hash_set_raninit(random()); + else +# endif + hash_set_raninit(random()); #endif rrl_mmap_init(nsd->child_count, nsd->options->rrl_size, nsd->options->rrl_ratelimit, @@ -1149,6 +1286,8 @@ server_shutdown(struct nsd *nsd) tsig_finalize(); #ifdef HAVE_SSL daemon_remote_delete(nsd->rc); /* ssl-delete secret keys */ + if (nsd->tls_ctx) + SSL_CTX_free(nsd->tls_ctx); #endif #ifdef MEMCLEAN /* OS collects memory pages */ @@ -1381,6 +1520,306 @@ server_send_soa_xfrd(struct nsd* nsd, int shortsoa) } } +#ifdef HAVE_SSL +void +log_crypto_err(const char* str) +{ + /* 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)); + log_msg(LOG_ERR, "%s crypto %s", str, buf); + while( (e=ERR_get_error()) ) { + ERR_error_string_n(e, buf, sizeof(buf)); + log_msg(LOG_ERR, "and additionally crypto %s", buf); + } +} + +void +perform_openssl_init(void) +{ + /* init SSL library */ +#ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS + ERR_load_crypto_strings(); +#endif + ERR_load_SSL_strings(); +#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO) + OpenSSL_add_all_algorithms(); +#else + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS + | OPENSSL_INIT_ADD_ALL_DIGESTS + | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); +#endif +#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) + (void)SSL_library_init(); +#else + OPENSSL_init_ssl(0, NULL); +#endif + + if(!RAND_status()) { + /* try to seed it */ + unsigned char buf[256]; + unsigned int v, seed=(unsigned)time(NULL) ^ (unsigned)getpid(); + size_t i; + v = seed; + for(i=0; i<256/sizeof(v); i++) { + memmove(buf+i*sizeof(v), &v, sizeof(v)); + v = v*seed + (unsigned int)i; + } + RAND_seed(buf, 256); + log_msg(LOG_WARNING, "warning: no entropy, seeding openssl PRNG with time"); + } +} + +static int +get_ocsp(char *filename, unsigned char **ocsp) +{ + BIO *bio; + OCSP_RESPONSE *response; + int len = -1; + unsigned char *p, *buf; + assert(filename); + + if ((bio = BIO_new_file(filename, "r")) == NULL) { + log_crypto_err("get_ocsp: BIO_new_file failed"); + return -1; + } + + if ((response = d2i_OCSP_RESPONSE_bio(bio, NULL)) == NULL) { + log_crypto_err("get_ocsp: d2i_OCSP_RESPONSE_bio failed"); + BIO_free(bio); + return -1; + } + + if ((len = i2d_OCSP_RESPONSE(response, NULL)) <= 0) { + log_crypto_err("get_ocsp: i2d_OCSP_RESPONSE #1 failed"); + OCSP_RESPONSE_free(response); + BIO_free(bio); + return -1; + } + + if ((buf = malloc((size_t) len)) == NULL) { + log_msg(LOG_ERR, "get_ocsp: malloc failed"); + OCSP_RESPONSE_free(response); + BIO_free(bio); + return -1; + } + + p = buf; + if ((len = i2d_OCSP_RESPONSE(response, &p)) <= 0) { + log_crypto_err("get_ocsp: i2d_OCSP_RESPONSE #2 failed"); + free(buf); + OCSP_RESPONSE_free(response); + BIO_free(bio); + return -1; + } + + OCSP_RESPONSE_free(response); + BIO_free(bio); + + *ocsp = buf; + return len; +} + +/* further setup ssl ctx after the keys are loaded */ +static void +listen_sslctx_setup_2(void* ctxt) +{ + SSL_CTX* ctx = (SSL_CTX*)ctxt; + (void)ctx; +#if HAVE_DECL_SSL_CTX_SET_ECDH_AUTO + 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) + if(1) { + EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1); + if (!ecdh) { + log_crypto_err("could not find p256, not enabling ECDHE"); + } else { + if (1 != SSL_CTX_set_tmp_ecdh (ctx, ecdh)) { + log_crypto_err("Error in SSL_CTX_set_tmp_ecdh, not enabling ECDHE"); + } + EC_KEY_free (ecdh); + } + } +#endif +} + +static int +add_ocsp_data_cb(SSL *s, void* ATTR_UNUSED(arg)) +{ + if(ocspdata) { + unsigned char *p; + if ((p=malloc(ocspdata_len)) == NULL) { + log_msg(LOG_ERR, "add_ocsp_data_cb: malloc failure"); + return SSL_TLSEXT_ERR_NOACK; + } + memcpy(p, ocspdata, ocspdata_len); + if ((SSL_set_tlsext_status_ocsp_resp(s, p, ocspdata_len)) != 1) { + log_crypto_err("Error in SSL_set_tlsext_status_ocsp_resp"); + free(p); + return SSL_TLSEXT_ERR_NOACK; + } + return SSL_TLSEXT_ERR_OK; + } else { + return SSL_TLSEXT_ERR_NOACK; + } +} + +SSL_CTX* +server_tls_ctx_setup(char* key, char* pem, char* verifypem) +{ + SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method()); + if(!ctx) { + log_crypto_err("could not SSL_CTX_new"); + return NULL; + } + /* no SSLv2, SSLv3 because has defects */ + if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2) != SSL_OP_NO_SSLv2){ + log_crypto_err("could not set SSL_OP_NO_SSLv2"); + SSL_CTX_free(ctx); + return NULL; + } + if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3) + != SSL_OP_NO_SSLv3){ + log_crypto_err("could not set SSL_OP_NO_SSLv3"); + SSL_CTX_free(ctx); + return 0; + } +#if defined(SSL_OP_NO_TLSv1) && defined(SSL_OP_NO_TLSv1_1) + /* if we have tls 1.1 disable 1.0 */ + if((SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1) & SSL_OP_NO_TLSv1) + != SSL_OP_NO_TLSv1){ + log_crypto_err("could not set SSL_OP_NO_TLSv1"); + SSL_CTX_free(ctx); + return 0; + } +#endif +#if defined(SSL_OP_NO_TLSv1_1) && defined(SSL_OP_NO_TLSv1_2) + /* if we have tls 1.2 disable 1.1 */ + if((SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1) & SSL_OP_NO_TLSv1_1) + != SSL_OP_NO_TLSv1_1){ + log_crypto_err("could not set SSL_OP_NO_TLSv1_1"); + SSL_CTX_free(ctx); + return 0; + } +#endif +#if defined(SSL_OP_NO_RENEGOTIATION) + /* disable client renegotiation */ + if((SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION) & + SSL_OP_NO_RENEGOTIATION) != SSL_OP_NO_RENEGOTIATION) { + log_crypto_err("could not set SSL_OP_NO_RENEGOTIATION"); + SSL_CTX_free(ctx); + return 0; + } +#endif +#if defined(SHA256_DIGEST_LENGTH) && defined(SSL_TXT_CHACHA20) + /* if we have sha256, set the cipher list to have no known vulns */ + if(!SSL_CTX_set_cipher_list(ctx, "ECDHE+AESGCM:ECDHE+CHACHA20")) + log_crypto_err("could not set cipher list with SSL_CTX_set_cipher_list"); +#endif + if((SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE) & + SSL_OP_CIPHER_SERVER_PREFERENCE) != + SSL_OP_CIPHER_SERVER_PREFERENCE) { + log_crypto_err("could not set SSL_OP_CIPHER_SERVER_PREFERENCE"); + SSL_CTX_free(ctx); + return 0; + } +#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL + SSL_CTX_set_security_level(ctx, 0); +#endif + if(!SSL_CTX_use_certificate_chain_file(ctx, pem)) { + log_msg(LOG_ERR, "error for cert file: %s", pem); + log_crypto_err("error in SSL_CTX use_certificate_chain_file"); + SSL_CTX_free(ctx); + return NULL; + } + if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) { + log_msg(LOG_ERR, "error for private key file: %s", key); + log_crypto_err("Error in SSL_CTX use_PrivateKey_file"); + SSL_CTX_free(ctx); + return NULL; + } + if(!SSL_CTX_check_private_key(ctx)) { + log_msg(LOG_ERR, "error for key file: %s", key); + log_crypto_err("Error in SSL_CTX check_private_key"); + SSL_CTX_free(ctx); + return NULL; + } + listen_sslctx_setup_2(ctx); + if(verifypem && verifypem[0]) { + if(!SSL_CTX_load_verify_locations(ctx, verifypem, NULL)) { + log_crypto_err("Error in SSL_CTX verify locations"); + SSL_CTX_free(ctx); + return NULL; + } + SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(verifypem)); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); + } + return ctx; +} + +SSL_CTX* +server_tls_ctx_create(struct nsd* nsd, char* verifypem, char* ocspfile) +{ + char *key, *pem; + SSL_CTX *ctx; + + key = nsd->options->tls_service_key; + pem = nsd->options->tls_service_pem; + if(!key || key[0] == 0) { + log_msg(LOG_ERR, "error: no tls-service-key file specified"); + return NULL; + } + if(!pem || pem[0] == 0) { + log_msg(LOG_ERR, "error: no tls-service-pem file specified"); + return NULL; + } + + /* NOTE:This mimics the existing code in Unbound 1.5.1 by supporting SSL but + * raft-ietf-uta-tls-bcp-08 recommends only using TLSv1.2*/ + ctx = server_tls_ctx_setup(key, pem, verifypem); + if(!ctx) { + log_msg(LOG_ERR, "could not setup server TLS context"); + return NULL; + } + if(ocspfile && ocspfile[0]) { + if ((ocspdata_len = get_ocsp(ocspfile, &ocspdata)) < 0) { + log_crypto_err("Error reading OCSPfile"); + SSL_CTX_free(ctx); + return NULL; + } else { + VERBOSITY(2, (LOG_INFO, "ocspfile %s loaded", ocspfile)); + if(!SSL_CTX_set_tlsext_status_cb(ctx, add_ocsp_data_cb)) { + log_crypto_err("Error in SSL_CTX_set_tlsext_status_cb"); + SSL_CTX_free(ctx); + return NULL; + } + } + } + return ctx; +} + +/* check if tcp_handler_accept_data created for TLS dedicated port */ +int +using_tls_port(struct sockaddr* addr, const char* tls_port) +{ + in_port_t port = 0; + + if (addr->sa_family == AF_INET) + port = ((struct sockaddr_in*)addr)->sin_port; +#ifndef HAVE_STRUCT_SOCKADDR_IN6 + else + port = ((struct sockaddr_in6*)addr)->sin6_port; +#endif /* HAVE_STRUCT_SOCKADDR_IN6 */ + if (atoi(tls_port) == ntohs(port)) + return 1; + + return 0; +} +#endif + /* pass timeout=-1 for blocking. Returns size, 0, -1(err), or -2(timeout) */ ssize_t block_read(struct nsd* nsd, int s, void* p, ssize_t sz, int timeout) @@ -2108,6 +2547,7 @@ server_child(struct nsd *nsd) handler = (struct event*) region_alloc( server_region, sizeof(*handler)); + memset(handler, 0, sizeof(*handler)); event_set(handler, nsd->this_child->parent_fd, EV_PERSIST| EV_READ, child_handle_parent_command, user_data); if(event_base_set(event_base, handler) != 0) @@ -2162,6 +2602,7 @@ server_child(struct nsd *nsd) 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) @@ -2187,6 +2628,20 @@ server_child(struct nsd *nsd) &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) @@ -2247,6 +2702,7 @@ server_child(struct nsd *nsd) } } + service_remaining_tcp(nsd); #ifdef BIND8_STATS bind8_stats(nsd); #endif /* BIND8_STATS */ @@ -2261,6 +2717,114 @@ server_child(struct nsd *nsd) server_shutdown(nsd); } +static void remaining_tcp_timeout(int ATTR_UNUSED(fd), short event, void* arg) +{ + int* timed_out = (int*)arg; + assert(event & EV_TIMEOUT); + /* wake up the service tcp thread, note event is no longer + * registered */ + *timed_out = 1; +} + +void +service_remaining_tcp(struct nsd* nsd) +{ + struct tcp_handler_data* p; + struct event_base* event_base; + /* check if it is needed */ + if(nsd->current_tcp_count == 0 || tcp_active_list == NULL) + return; + VERBOSITY(4, (LOG_INFO, "service remaining TCP connections")); + + /* setup event base */ + event_base = nsd_child_event_base(); + if(!event_base) { + log_msg(LOG_ERR, "nsd remain tcp could not create event base"); + return; + } + /* register tcp connections */ + for(p = tcp_active_list; p != NULL; p = p->next) { + struct timeval timeout; + int fd = p->event.ev_fd; +#ifdef USE_MINI_EVENT + short event = p->event.ev_flags & (EV_READ|EV_WRITE); +#else + short event = p->event.ev_events & (EV_READ|EV_WRITE); +#endif + void (*fn)(int, short, void*); +#ifdef HAVE_SSL + if(p->tls) { + if((event&EV_READ)) + fn = handle_tls_reading; + else fn = handle_tls_writing; + } else { +#endif + if((event&EV_READ)) + fn = handle_tcp_reading; + else fn = handle_tcp_writing; +#ifdef HAVE_SSL + } +#endif + + /* set timeout to 1/10 second */ + if(p->tcp_timeout > 100) + p->tcp_timeout = 100; + timeout.tv_sec = p->tcp_timeout / 1000; + timeout.tv_usec = (p->tcp_timeout % 1000)*1000; + event_del(&p->event); + memset(&p->event, 0, sizeof(p->event)); + event_set(&p->event, fd, EV_PERSIST | event | EV_TIMEOUT, + fn, p); + if(event_base_set(event_base, &p->event) != 0) + log_msg(LOG_ERR, "event base set failed"); + if(event_add(&p->event, &timeout) != 0) + log_msg(LOG_ERR, "event add failed"); + } + + /* handle it */ + while(nsd->current_tcp_count > 0) { + mode_t m = server_signal_mode(nsd); + struct event timeout; + struct timeval tv; + int timed_out = 0; + if(m == NSD_QUIT || m == NSD_SHUTDOWN || + m == NSD_REAP_CHILDREN) { + /* quit */ + break; + } + /* timer */ + /* have to do something every second */ + tv.tv_sec = 1; + tv.tv_usec = 0; + memset(&timeout, 0, sizeof(timeout)); + event_set(&timeout, -1, EV_TIMEOUT, remaining_tcp_timeout, + &timed_out); + if(event_base_set(event_base, &timeout) != 0) + log_msg(LOG_ERR, "remaintcp timer: event_base_set failed"); + if(event_add(&timeout, &tv) != 0) + log_msg(LOG_ERR, "remaintcp timer: event_add failed"); + + /* service loop */ + if(event_base_loop(event_base, EVLOOP_ONCE) == -1) { + if (errno != EINTR) { + log_msg(LOG_ERR, "dispatch failed: %s", strerror(errno)); + break; + } + } + if(!timed_out) { + event_del(&timeout); + } else { + /* timed out, quit */ + VERBOSITY(4, (LOG_INFO, "service remaining TCP connections: timed out, quit")); + break; + } + } +#ifdef MEMCLEAN + event_base_free(event_base); +#endif + /* continue to quit after return */ +} + #if defined(HAVE_SENDMMSG) && !defined(NONBLOCKING_IS_BROKEN) && defined(HAVE_RECVMMSG) static void handle_udp(int fd, short event, void* arg) @@ -2552,12 +3116,48 @@ handle_udp(int fd, short event, void* arg) } #endif /* defined(HAVE_SENDMMSG) && !defined(NONBLOCKING_IS_BROKEN) && defined(HAVE_RECVMMSG) */ +#ifdef HAVE_SSL +/* + * Setup an event for the tcp handler. + */ +static void +tcp_handler_setup_event(struct tcp_handler_data* data, void (*fn)(int, short, void *), + int fd, short event) +{ + struct timeval timeout; + struct event_base* ev_base; + + timeout.tv_sec = data->nsd->tcp_timeout; + timeout.tv_usec = 0L; + + ev_base = data->event.ev_base; + event_del(&data->event); + memset(&data->event, 0, sizeof(data->event)); + event_set(&data->event, fd, event, fn, data); + if(event_base_set(ev_base, &data->event) != 0) + log_msg(LOG_ERR, "event base set failed"); + if(event_add(&data->event, &timeout) != 0) + log_msg(LOG_ERR, "event add failed"); +} +#endif /* HAVE_SSL */ static void cleanup_tcp_handler(struct tcp_handler_data* data) { event_del(&data->event); +#ifdef HAVE_SSL + if(data->tls) { + SSL_shutdown(data->tls); + SSL_free(data->tls); + data->tls = NULL; + } +#endif close(data->event.ev_fd); + if(data->prev) + data->prev->next = data->next; + else tcp_active_list = data->next; + if(data->next) + data->next->prev = data->prev; /* * Enable the TCP accept handlers when the current number of @@ -2593,7 +3193,7 @@ handle_tcp_reading(int fd, short event, void* arg) if (data->nsd->tcp_query_count > 0 && data->query_count >= data->nsd->tcp_query_count) { - /* No more queries allowed on this tcp connection. */ + /* No more queries allowed on this tcp connection. */ cleanup_tcp_handler(data); return; } @@ -2770,6 +3370,15 @@ handle_tcp_reading(int fd, short event, void* arg) /* Switch to the tcp write handler. */ buffer_flip(data->query->packet); data->query->tcplen = buffer_remaining(data->query->packet); +#ifdef BIND8_STATS + /* Account the rcode & TC... */ + STATUP2(data->nsd, rcode, RCODE(data->query->packet)); + ZTATUP2(data->nsd, data->query->zone, rcode, RCODE(data->query->packet)); + if (TC(data->query->packet)) { + STATUP(data->nsd, truncated); + ZTATUP(data->nsd, data->query->zone, truncated); + } +#endif /* BIND8_STATS */ #ifdef USE_DNSTAP dt_collector_submit_auth_response(data->nsd, &data->query->addr, data->query->addrlen, data->query->tcp, data->query->packet, @@ -2782,8 +3391,9 @@ handle_tcp_reading(int fd, short event, void* arg) ev_base = data->event.ev_base; event_del(&data->event); - event_set(&data->event, fd, EV_PERSIST | EV_WRITE | EV_TIMEOUT, - handle_tcp_writing, data); + memset(&data->event, 0, sizeof(data->event)); + event_set(&data->event, fd, EV_PERSIST | EV_READ | EV_TIMEOUT, + handle_tcp_reading, data); if(event_base_set(ev_base, &data->event) != 0) log_msg(LOG_ERR, "event base set tcpr failed"); if(event_add(&data->event, &timeout) != 0) @@ -2914,6 +3524,7 @@ handle_tcp_writing(int fd, short event, void* arg) timeout.tv_usec = (data->tcp_timeout % 1000)*1000; ev_base = data->event.ev_base; event_del(&data->event); + memset(&data->event, 0, sizeof(data->event)); event_set(&data->event, fd, EV_PERSIST | EV_WRITE | EV_TIMEOUT, handle_tcp_writing, data); if(event_base_set(ev_base, &data->event) != 0) @@ -2945,6 +3556,7 @@ handle_tcp_writing(int fd, short event, void* arg) timeout.tv_usec = (data->tcp_timeout % 1000)*1000; ev_base = data->event.ev_base; event_del(&data->event); + memset(&data->event, 0, sizeof(data->event)); event_set(&data->event, fd, EV_PERSIST | EV_READ | EV_TIMEOUT, handle_tcp_reading, data); if(event_base_set(ev_base, &data->event) != 0) @@ -2953,6 +3565,428 @@ handle_tcp_writing(int fd, short event, void* arg) log_msg(LOG_ERR, "event add tcpw failed"); } +#ifdef HAVE_SSL +/** create SSL object and associate fd */ +static SSL* +incoming_ssl_fd(SSL_CTX* ctx, int fd) +{ + SSL* ssl = SSL_new((SSL_CTX*)ctx); + if(!ssl) { + log_crypto_err("could not SSL_new"); + return NULL; + } + SSL_set_accept_state(ssl); + (void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); + if(!SSL_set_fd(ssl, fd)) { + log_crypto_err("could not SSL_set_fd"); + SSL_free(ssl); + return NULL; + } + return ssl; +} + +/** TLS handshake to upgrade TCP connection */ +static int +tls_handshake(struct tcp_handler_data* data, int fd, int writing) +{ + int r; + if(data->shake_state == tls_hs_read_event) { + /* read condition satisfied back to writing */ + tcp_handler_setup_event(data, handle_tls_writing, fd, EV_PERSIST|EV_TIMEOUT|EV_WRITE); + data->shake_state = tls_hs_none; + return 1; + } + if(data->shake_state == tls_hs_write_event) { + /* write condition satisfied back to reading */ + tcp_handler_setup_event(data, handle_tls_reading, fd, EV_PERSIST|EV_TIMEOUT|EV_READ); + data->shake_state = tls_hs_none; + return 1; + } + + /* (continue to) setup the TLS connection */ + ERR_clear_error(); + r = SSL_do_handshake(data->tls); + + if(r != 1) { + int want = SSL_get_error(data->tls, r); + if(want == SSL_ERROR_WANT_READ) { + if(data->shake_state == tls_hs_read) { + /* try again later */ + return 1; + } + data->shake_state = tls_hs_read; + /* switch back to reading mode */ + tcp_handler_setup_event(data, handle_tls_reading, fd, EV_PERSIST|EV_TIMEOUT|EV_READ); + return 1; + } else if(want == SSL_ERROR_WANT_WRITE) { + if(data->shake_state == tls_hs_write) { + /* try again later */ + return 1; + } + data->shake_state = tls_hs_write; + /* switch back to writing mode */ + tcp_handler_setup_event(data, handle_tls_writing, fd, EV_PERSIST|EV_TIMEOUT|EV_WRITE); + return 1; + } else { + if(r == 0) + VERBOSITY(3, (LOG_ERR, "TLS handshake: connection closed prematurely")); + cleanup_tcp_handler(data); + VERBOSITY(3, (LOG_ERR, "TLS handshake failed")); + return 0; + } + } + + /* Use to log successful upgrade for testing - could be removed*/ + VERBOSITY(3, (LOG_INFO, "TLS handshake succeeded.")); + /* set back to the event we need to have when reading (or writing) */ + if(data->shake_state == tls_hs_read && writing) { + tcp_handler_setup_event(data, handle_tls_writing, fd, EV_PERSIST|EV_TIMEOUT|EV_WRITE); + } else if(data->shake_state == tls_hs_write && !writing) { + tcp_handler_setup_event(data, handle_tls_reading, fd, EV_PERSIST|EV_TIMEOUT|EV_READ); + } + data->shake_state = tls_hs_none; + return 1; +} + +/** handle TLS reading of incoming query */ +static void +handle_tls_reading(int fd, short event, void* arg) +{ + struct tcp_handler_data *data = (struct tcp_handler_data *) arg; + ssize_t received; + + if ((event & EV_TIMEOUT)) { + /* Connection timed out. */ + cleanup_tcp_handler(data); + return; + } + + if (data->nsd->tcp_query_count > 0 && + data->query_count >= data->nsd->tcp_query_count) { + /* No more queries allowed on this tcp connection. */ + cleanup_tcp_handler(data); + return; + } + + assert((event & EV_READ)); + + if (data->bytes_transmitted == 0) { + query_reset(data->query, TCP_MAX_MESSAGE_LEN, 1); + } + + if(data->shake_state != tls_hs_none) { + if(!tls_handshake(data, fd, 0)) + return; + if(data->shake_state != tls_hs_none) + return; + } + + /* + * Check if we received the leading packet length bytes yet. + */ + if(data->bytes_transmitted < sizeof(uint16_t)) { + ERR_clear_error(); + if((received=SSL_read(data->tls, (char *) &data->query->tcplen + + data->bytes_transmitted, + sizeof(uint16_t) - data->bytes_transmitted)) <= 0) { + int want = SSL_get_error(data->tls, received); + if(want == SSL_ERROR_ZERO_RETURN) { + cleanup_tcp_handler(data); + return; /* shutdown, closed */ + } else if(want == SSL_ERROR_WANT_READ) { + /* wants to be called again */ + return; + } + else if(want == SSL_ERROR_WANT_WRITE) { + /* switch to writing */ + data->shake_state = tls_hs_write_event; + tcp_handler_setup_event(data, handle_tls_writing, fd, EV_PERSIST | EV_WRITE | EV_TIMEOUT); + return; + } + cleanup_tcp_handler(data); + log_crypto_err("could not SSL_read"); + return; + } + + data->bytes_transmitted += received; + if (data->bytes_transmitted < sizeof(uint16_t)) { + /* + * Not done with the tcplen yet, wait for more + * data to become available. + */ + return; + } + + assert(data->bytes_transmitted == sizeof(uint16_t)); + + data->query->tcplen = ntohs(data->query->tcplen); + + /* + * Minimum query size is: + * + * Size of the header (12) + * + Root domain name (1) + * + Query class (2) + * + Query type (2) + */ + if (data->query->tcplen < QHEADERSZ + 1 + sizeof(uint16_t) + sizeof(uint16_t)) { + VERBOSITY(2, (LOG_WARNING, "packet too small, dropping tcp connection")); + cleanup_tcp_handler(data); + return; + } + + if (data->query->tcplen > data->query->maxlen) { + VERBOSITY(2, (LOG_WARNING, "insufficient tcp buffer, dropping connection")); + cleanup_tcp_handler(data); + return; + } + + buffer_set_limit(data->query->packet, data->query->tcplen); + } + + assert(buffer_remaining(data->query->packet) > 0); + + /* Read the (remaining) query data. */ + ERR_clear_error(); + received = SSL_read(data->tls, (void*)buffer_current(data->query->packet), + (int)buffer_remaining(data->query->packet)); + if(received <= 0) { + int want = SSL_get_error(data->tls, received); + if(want == SSL_ERROR_ZERO_RETURN) { + cleanup_tcp_handler(data); + return; /* shutdown, closed */ + } else if(want == SSL_ERROR_WANT_READ) { + /* wants to be called again */ + return; + } + else if(want == SSL_ERROR_WANT_WRITE) { + /* switch back writing */ + data->shake_state = tls_hs_write_event; + tcp_handler_setup_event(data, handle_tls_writing, fd, EV_PERSIST | EV_WRITE | EV_TIMEOUT); + return; + } + cleanup_tcp_handler(data); + log_crypto_err("could not SSL_read"); + return; + } + + data->bytes_transmitted += received; + buffer_skip(data->query->packet, received); + if (buffer_remaining(data->query->packet) > 0) { + /* + * Message not yet complete, wait for more data to + * become available. + */ + return; + } + + assert(buffer_position(data->query->packet) == data->query->tcplen); + + /* Account... */ +#ifndef INET6 + STATUP(data->nsd, ctls); +#else + if (data->query->addr.ss_family == AF_INET) { + STATUP(data->nsd, ctls); + } else if (data->query->addr.ss_family == AF_INET6) { + STATUP(data->nsd, ctls6); + } +#endif + + /* We have a complete query, process it. */ + + /* tcp-query-count: handle query counter ++ */ + data->query_count++; + + buffer_flip(data->query->packet); +#ifdef USE_DNSTAP + dt_collector_submit_auth_query(data->nsd, &data->query->addr, + data->query->addrlen, data->query->tcp, data->query->packet); +#endif /* USE_DNSTAP */ + data->query_state = server_process_query(data->nsd, data->query); + if (data->query_state == QUERY_DISCARDED) { + /* Drop the packet and the entire connection... */ + STATUP(data->nsd, dropped); + ZTATUP(data->nsd, data->query->zone, dropped); + cleanup_tcp_handler(data); + return; + } + +#ifdef BIND8_STATS + if (RCODE(data->query->packet) == RCODE_OK + && !AA(data->query->packet)) + { + STATUP(data->nsd, nona); + ZTATUP(data->nsd, data->query->zone, nona); + } +#endif /* BIND8_STATS */ + +#ifdef USE_ZONE_STATS +#ifndef INET6 + ZTATUP(data->nsd, data->query->zone, ctls); +#else + if (data->query->addr.ss_family == AF_INET) { + ZTATUP(data->nsd, data->query->zone, ctls); + } else if (data->query->addr.ss_family == AF_INET6) { + ZTATUP(data->nsd, data->query->zone, ctls6); + } +#endif +#endif /* USE_ZONE_STATS */ + + query_add_optional(data->query, data->nsd); + + /* Switch to the tcp write handler. */ + buffer_flip(data->query->packet); + data->query->tcplen = buffer_remaining(data->query->packet); +#ifdef BIND8_STATS + /* Account the rcode & TC... */ + STATUP2(data->nsd, rcode, RCODE(data->query->packet)); + ZTATUP2(data->nsd, data->query->zone, rcode, RCODE(data->query->packet)); + if (TC(data->query->packet)) { + STATUP(data->nsd, truncated); + ZTATUP(data->nsd, data->query->zone, truncated); + } +#endif /* BIND8_STATS */ +#ifdef USE_DNSTAP + dt_collector_submit_auth_response(data->nsd, &data->query->addr, + data->query->addrlen, data->query->tcp, data->query->packet, + data->query->zone); +#endif /* USE_DNSTAP */ + data->bytes_transmitted = 0; + + tcp_handler_setup_event(data, handle_tls_writing, fd, EV_PERSIST | EV_WRITE | EV_TIMEOUT); + + /* see if we can write the answer right away(usually so,EAGAIN ifnot)*/ + handle_tls_writing(fd, EV_WRITE, data); +} + +/** handle TLS writing of outgoing response */ +static void +handle_tls_writing(int fd, short event, void* arg) +{ + struct tcp_handler_data *data = (struct tcp_handler_data *) arg; + ssize_t sent; + struct query *q = data->query; + /* static variable that holds reassembly buffer used to put the + * TCP length in front of the packet, like writev. */ + static buffer_type* global_tls_temp_buffer = NULL; + buffer_type* write_buffer; + + if ((event & EV_TIMEOUT)) { + /* Connection timed out. */ + cleanup_tcp_handler(data); + return; + } + + assert((event & EV_WRITE)); + + if(data->shake_state != tls_hs_none) { + if(!tls_handshake(data, fd, 1)) + return; + if(data->shake_state != tls_hs_none) + return; + } + + (void)SSL_set_mode(data->tls, SSL_MODE_ENABLE_PARTIAL_WRITE); + + /* If we are writing the start of a message, we must include the length + * this is done with a copy into write_buffer. */ + write_buffer = NULL; + if (data->bytes_transmitted == 0) { + if(!global_tls_temp_buffer) { + /* gets deallocated when nsd shuts down from + * nsd.region */ + global_tls_temp_buffer = buffer_create(nsd.region, + QIOBUFSZ + sizeof(q->tcplen)); + if (!global_tls_temp_buffer) { + return; + } + } + write_buffer = global_tls_temp_buffer; + buffer_clear(write_buffer); + buffer_write_u16(write_buffer, q->tcplen); + buffer_write(write_buffer, buffer_current(q->packet), + (int)buffer_remaining(q->packet)); + buffer_flip(write_buffer); + } else { + write_buffer = q->packet; + } + + /* Write the response */ + ERR_clear_error(); + sent = SSL_write(data->tls, buffer_current(write_buffer), buffer_remaining(write_buffer)); + if(sent <= 0) { + int want = SSL_get_error(data->tls, sent); + if(want == SSL_ERROR_ZERO_RETURN) { + cleanup_tcp_handler(data); + /* closed */ + } else if(want == SSL_ERROR_WANT_READ) { + /* switch back to reading */ + data->shake_state = tls_hs_read_event; + tcp_handler_setup_event(data, handle_tls_reading, fd, EV_PERSIST | EV_READ | EV_TIMEOUT); + } else if(want != SSL_ERROR_WANT_WRITE) { + cleanup_tcp_handler(data); + log_crypto_err("could not SSL_write"); + } + return; + } + + buffer_skip(write_buffer, sent); + if(buffer_remaining(write_buffer) != 0) { + /* If not all sent, sync up the real buffer if it wasn't used.*/ + if (data->bytes_transmitted == 0 && (ssize_t)sent > (ssize_t)sizeof(q->tcplen)) { + buffer_skip(q->packet, (ssize_t)sent - (ssize_t)sizeof(q->tcplen)); + } + } + + data->bytes_transmitted += sent; + if (data->bytes_transmitted < q->tcplen + sizeof(q->tcplen)) { + /* + * Still more data to write when socket becomes + * writable again. + */ + return; + } + + assert(data->bytes_transmitted == q->tcplen + sizeof(q->tcplen)); + + if (data->query_state == QUERY_IN_AXFR) { + /* Continue processing AXFR and writing back results. */ + buffer_clear(q->packet); + data->query_state = query_axfr(data->nsd, q); + if (data->query_state != QUERY_PROCESSED) { + query_add_optional(data->query, data->nsd); + + /* Reset data. */ + buffer_flip(q->packet); + q->tcplen = buffer_remaining(q->packet); + data->bytes_transmitted = 0; + /* Reset to writing mode. */ + tcp_handler_setup_event(data, handle_tls_writing, fd, EV_PERSIST | EV_WRITE | EV_TIMEOUT); + + /* + * Write data if/when the socket is writable + * again. + */ + return; + } + } + + /* + * Done sending, wait for the next request to arrive on the + * TCP socket by installing the TCP read handler. + */ + if (data->nsd->tcp_query_count > 0 && + data->query_count >= data->nsd->tcp_query_count) { + + (void) shutdown(fd, SHUT_WR); + } + + data->bytes_transmitted = 0; + + tcp_handler_setup_event(data, handle_tls_reading, fd, EV_PERSIST | EV_READ | EV_TIMEOUT); +} +#endif static void handle_slowaccept_timeout(int ATTR_UNUSED(fd), short ATTR_UNUSED(event), @@ -2964,6 +3998,26 @@ handle_slowaccept_timeout(int ATTR_UNUSED(fd), short ATTR_UNUSED(event), } } +static int perform_accept(int fd, struct sockaddr *addr, socklen_t *addrlen) +{ +#ifndef HAVE_ACCEPT4 + int s = accept(fd, addr, addrlen); + if (s != -1) { + if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) { + log_msg(LOG_ERR, "fcntl failed: %s", strerror(errno)); + close(s); + s = -1; + errno=EINTR; /* stop error printout as error in accept4 + by setting this errno, it omits printout, in + later code that calls nsd_accept4 */ + } + } + return s; +#else + return accept4(fd, addr, addrlen, SOCK_NONBLOCK); +#endif /* HAVE_ACCEPT4 */ +} + /* * Handle an incoming TCP connection. The connection is accepted and * a new TCP reader event handler is added. The TCP handler @@ -2975,6 +4029,7 @@ handle_tcp_accept(int fd, short event, void* arg) struct tcp_accept_handler_data *data = (struct tcp_accept_handler_data *) arg; int s; + int reject = 0; struct tcp_handler_data *tcp_data; region_type *tcp_region; #ifdef INET6 @@ -2990,16 +4045,15 @@ handle_tcp_accept(int fd, short event, void* arg) } if (data->nsd->current_tcp_count >= data->nsd->maximum_tcp_count) { - return; + reject = data->nsd->options->tcp_reject_overflow; + if (!reject) { + return; + } } /* Accept it... */ addrlen = sizeof(addr); -#ifndef HAVE_ACCEPT4 - s = accept(fd, (struct sockaddr *) &addr, &addrlen); -#else - s = accept4(fd, (struct sockaddr *) &addr, &addrlen, SOCK_NONBLOCK); -#endif + s = perform_accept(fd, (struct sockaddr *) &addr, &addrlen); if (s == -1) { /** * EMFILE and ENFILE is a signal that the limit of open @@ -3014,6 +4068,8 @@ handle_tcp_accept(int fd, short event, void* arg) configure_handler_event_types(0); tv.tv_sec = SLOW_ACCEPT_TIMEOUT; tv.tv_usec = 0L; + memset(&slowaccept_event, 0, + sizeof(slowaccept_event)); event_set(&slowaccept_event, -1, EV_TIMEOUT, handle_slowaccept_timeout, NULL); (void)event_base_set(data->event.ev_base, @@ -3036,13 +4092,11 @@ handle_tcp_accept(int fd, short event, void* arg) return; } -#ifndef HAVE_ACCEPT4 - if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) { - log_msg(LOG_ERR, "fcntl failed: %s", strerror(errno)); + if (reject) { + shutdown(s, SHUT_RDWR); close(s); return; } -#endif /* * This region is deallocated when the TCP connection is @@ -3056,6 +4110,12 @@ handle_tcp_accept(int fd, short event, void* arg) compression_table_size, compressed_dnames); tcp_data->nsd = data->nsd; tcp_data->query_count = 0; +#ifdef HAVE_SSL + tcp_data->shake_state = tls_hs_none; + tcp_data->tls = NULL; +#endif + tcp_data->prev = NULL; + tcp_data->next = NULL; tcp_data->query_state = QUERY_PROCESSED; tcp_data->bytes_transmitted = 0; @@ -3067,11 +4127,29 @@ handle_tcp_accept(int fd, short event, void* arg) /* very busy, give smaller timeout */ tcp_data->tcp_timeout = 200; } + memset(&tcp_data->event, 0, sizeof(tcp_data->event)); timeout.tv_sec = tcp_data->tcp_timeout / 1000; timeout.tv_usec = (tcp_data->tcp_timeout % 1000)*1000; - event_set(&tcp_data->event, s, EV_PERSIST | EV_READ | EV_TIMEOUT, - handle_tcp_reading, tcp_data); +#ifdef HAVE_SSL + if (data->tls_accept) { + tcp_data->tls = incoming_ssl_fd(tcp_data->nsd->tls_ctx, s); + if(!tcp_data->tls) { + close(s); + return; + } + tcp_data->shake_state = tls_hs_read; + memset(&tcp_data->event, 0, sizeof(tcp_data->event)); + event_set(&tcp_data->event, s, EV_PERSIST | EV_READ | EV_TIMEOUT, + handle_tls_reading, tcp_data); + } else { +#endif + memset(&tcp_data->event, 0, sizeof(tcp_data->event)); + event_set(&tcp_data->event, s, EV_PERSIST | EV_READ | EV_TIMEOUT, + handle_tcp_reading, tcp_data); +#ifdef HAVE_SSL + } +#endif if(event_base_set(data->event.ev_base, &tcp_data->event) != 0) { log_msg(LOG_ERR, "cannot set tcp event base"); close(s); @@ -3084,14 +4162,26 @@ handle_tcp_accept(int fd, short event, void* arg) region_destroy(tcp_region); return; } + if(tcp_active_list) { + tcp_active_list->prev = tcp_data; + tcp_data->next = tcp_active_list; + } + tcp_active_list = tcp_data; /* * Keep track of the total number of TCP handlers installed so * we can stop accepting connections when the maximum number * of simultaneous TCP connections is reached. + * + * If tcp-reject-overflow is enabled, however, then we do not + * change the handler event type; we keep it as-is and accept + * overflow TCP connections only so that we can forcibly kill + * them off. */ ++data->nsd->current_tcp_count; - if (data->nsd->current_tcp_count == data->nsd->maximum_tcp_count) { + if (!data->nsd->options->tcp_reject_overflow && + data->nsd->current_tcp_count == data->nsd->maximum_tcp_count) + { configure_handler_event_types(0); } } @@ -3165,6 +4255,7 @@ configure_handler_event_types(short event_types) struct event_base* base = handler->ev_base; if(tcp_accept_handlers[i].event_added) event_del(handler); + memset(handler, 0, sizeof(*handler)); event_set(handler, fd, event_types, handle_tcp_accept, &tcp_accept_handlers[i]); if(event_base_set(base, handler) != 0) diff --git a/usr.sbin/nsd/tsig.c b/usr.sbin/nsd/tsig.c index b0e40116f74..a450a8b3029 100644 --- a/usr.sbin/nsd/tsig.c +++ b/usr.sbin/nsd/tsig.c @@ -19,6 +19,61 @@ #include "query.h" #include "rbtree.h" +#ifndef HAVE_SSL +/* we need fixed time compare */ +#define CRYPTO_memcmp memcmp_fixedtime +int memcmp_fixedtime(const void *s1, const void *s2, size_t n) +{ + size_t i; + const uint8_t* u1 = (const uint8_t*)s1; + const uint8_t* u2 = (const uint8_t*)s2; + int ret = 0, haveit = 0, bret = 0, bhaveit = 0; + /* this routine loops for every byte in the strings. + * every loop, it tests ==, < and >. All three. One succeeds, + * as every time it must be equal, smaller or larger. The one + * that succeeds has one if-comparison and two assignments. */ + for(i=0; i<n; i++) { + if(u1[i] == u2[i]) { + /* waste time equal to < and > statements */ + if(haveit) { + bret = -1; /* waste time */ + bhaveit = 1; + } else { + bret = 1; /* waste time */ + bhaveit = 1; + } + } + if(u1[i] < u2[i]) { + if(haveit) { + bret = -1; /* waste time equal to the else */ + bhaveit = 1; + } else { + ret = -1; + haveit = 1; + } + } + if(u1[i] > u2[i]) { + if(haveit) { + bret = 1; /* waste time equal to the else */ + bhaveit = 1; + } else { + ret = 1; + haveit = 1; + } + } + } + /* use the variables to stop the compiler from excluding them */ + if(bhaveit) { + if(bret == -2) + ret = 0; /* never happens */ + } else { + if(bret == -2) + ret = 0; /* never happens */ + } + return ret; +} +#endif + static region_type *tsig_region; struct tsig_key_table diff --git a/usr.sbin/nsd/util.c b/usr.sbin/nsd/util.c index 1d59470f2c3..bef11f179c1 100644 --- a/usr.sbin/nsd/util.c +++ b/usr.sbin/nsd/util.c @@ -1087,6 +1087,35 @@ addr2str( } void +addrport2str( +#ifdef INET6 + struct sockaddr_storage *addr +#else + struct sockaddr_in *addr +#endif + , char* str, size_t len) +{ + char ip[256]; +#ifdef INET6 + if (addr->ss_family == AF_INET6) { + if (!inet_ntop(AF_INET6, + &((struct sockaddr_in6 *)addr)->sin6_addr, ip, sizeof(ip))) + strlcpy(ip, "[unknown ip6, inet_ntop failed]", sizeof(ip)); + /* append port number */ + snprintf(str, len, "%s@%u", ip, + (unsigned)ntohs(((struct sockaddr_in6 *)addr)->sin6_port)); + return; + } else +#endif + if (!inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, + ip, sizeof(ip))) + strlcpy(ip, "[unknown ip4, inet_ntop failed]", sizeof(ip)); + /* append port number */ + snprintf(str, len, "%s@%u", ip, + (unsigned)ntohs(((struct sockaddr_in *)addr)->sin_port)); +} + +void append_trailing_slash(const char** dirname, region_type* region) { int l = strlen(*dirname); diff --git a/usr.sbin/nsd/util.h b/usr.sbin/nsd/util.h index 3cca1f2ead8..63c2d771b6d 100644 --- a/usr.sbin/nsd/util.h +++ b/usr.sbin/nsd/util.h @@ -410,6 +410,15 @@ void addr2str( #endif , char* str, size_t len); +/* print addr@port */ +void addrport2str( +#ifdef INET6 + struct sockaddr_storage *addr +#else + struct sockaddr_in *addr +#endif + , char* str, size_t len); + /** copy dirname string and append slash. Previous dirname is leaked, * but it is to be used once, at startup, for chroot */ void append_trailing_slash(const char** dirname, struct region* region); diff --git a/usr.sbin/nsd/xfrd-disk.c b/usr.sbin/nsd/xfrd-disk.c index 6ae8cd6249b..180302bbf17 100644 --- a/usr.sbin/nsd/xfrd-disk.c +++ b/usr.sbin/nsd/xfrd-disk.c @@ -289,6 +289,13 @@ xfrd_read_state(struct xfrd_state* xfrd) zone->state = xfrd_zone_refreshing; xfrd_set_refresh_now(zone); } + if(timeout != 0 && filetime + timeout < (uint32_t)xfrd_time()) { + /* timeout is in the past, refresh the zone */ + timeout = 0; + if(zone->state == xfrd_zone_ok) + zone->state = xfrd_zone_refreshing; + xfrd_set_refresh_now(zone); + } /* There is a soa && current time is past expiry point */ if(soa_disk_acquired_read!=0 && diff --git a/usr.sbin/nsd/xfrd-notify.c b/usr.sbin/nsd/xfrd-notify.c index 3f9853e6409..280ac7ff701 100644 --- a/usr.sbin/nsd/xfrd-notify.c +++ b/usr.sbin/nsd/xfrd-notify.c @@ -382,6 +382,8 @@ notify_setup_event(struct notify_zone* zone) event_del(&zone->notify_send_handler); } zone->notify_timeout.tv_sec = XFRD_NOTIFY_RETRY_TIMOUT; + memset(&zone->notify_send_handler, 0, + sizeof(zone->notify_send_handler)); event_set(&zone->notify_send_handler, fd, EV_READ | EV_TIMEOUT, xfrd_handle_notify_send, zone); if(event_base_set(xfrd->event_base, &zone->notify_send_handler) != 0) @@ -396,6 +398,8 @@ notify_setup_event(struct notify_zone* zone) event_del(&zone->notify_send6_handler); } zone->notify_timeout.tv_sec = XFRD_NOTIFY_RETRY_TIMOUT; + memset(&zone->notify_send6_handler, 0, + sizeof(zone->notify_send6_handler)); event_set(&zone->notify_send6_handler, fd, EV_READ | EV_TIMEOUT, xfrd_handle_notify_send, zone); if(event_base_set(xfrd->event_base, &zone->notify_send6_handler) != 0) @@ -465,6 +469,8 @@ setup_notify_active(struct notify_zone* zone) if(zone->notify_send_enable) notify_send_disable(zone); + memset(&zone->notify_send_handler, 0, + sizeof(zone->notify_send_handler)); event_set(&zone->notify_send_handler, -1, EV_TIMEOUT, xfrd_handle_notify_send, zone); if(event_base_set(xfrd->event_base, &zone->notify_send_handler) != 0) diff --git a/usr.sbin/nsd/xfrd-tcp.c b/usr.sbin/nsd/xfrd-tcp.c index 3c176a38ff8..1ae5311c538 100644 --- a/usr.sbin/nsd/xfrd-tcp.c +++ b/usr.sbin/nsd/xfrd-tcp.c @@ -330,6 +330,7 @@ tcp_pipe_reset_timeout(struct xfrd_tcp_pipeline* tp) tv.tv_usec = 0; if(tp->handler_added) event_del(&tp->handler); + memset(&tp->handler, 0, sizeof(tp->handler)); event_set(&tp->handler, fd, EV_PERSIST|EV_TIMEOUT|EV_READ| (tp->tcp_send_first?EV_WRITE:0), xfrd_handle_tcp_pipe, tp); if(event_base_set(xfrd->event_base, &tp->handler) != 0) @@ -575,6 +576,7 @@ xfrd_tcp_open(struct xfrd_tcp_set* set, struct xfrd_tcp_pipeline* tp, /* set the tcp pipe event */ if(tp->handler_added) event_del(&tp->handler); + memset(&tp->handler, 0, sizeof(tp->handler)); event_set(&tp->handler, fd, EV_PERSIST|EV_TIMEOUT|EV_READ|EV_WRITE, xfrd_handle_tcp_pipe, tp); if(event_base_set(xfrd->event_base, &tp->handler) != 0) diff --git a/usr.sbin/nsd/xfrd.c b/usr.sbin/nsd/xfrd.c index c2eeff8a1e8..3865747a660 100644 --- a/usr.sbin/nsd/xfrd.c +++ b/usr.sbin/nsd/xfrd.c @@ -170,6 +170,7 @@ xfrd_init(int socket, struct nsd* nsd, int shortsoa, int reload_active, xfrd->child_timer_added = 0; xfrd->ipc_send_blocked = 0; + memset(&xfrd->ipc_handler, 0, sizeof(xfrd->ipc_handler)); event_set(&xfrd->ipc_handler, socket, EV_PERSIST|EV_READ, xfrd_handle_ipc, xfrd); if(event_base_set(xfrd->event_base, &xfrd->ipc_handler) != 0) @@ -294,6 +295,7 @@ xfrd_sig_process(void) struct timeval tv; tv.tv_sec = XFRD_CHILD_REAP_TIMEOUT; tv.tv_usec = 0; + memset(&xfrd->child_timer, 0, sizeof(xfrd->child_timer)); event_set(&xfrd->child_timer, -1, EV_TIMEOUT, xfrd_handle_child_timer, xfrd); if(event_base_set(xfrd->event_base, &xfrd->child_timer) != 0) @@ -400,6 +402,8 @@ xfrd_shutdown() xfrd_del_tempdir(xfrd->nsd); #ifdef HAVE_SSL daemon_remote_delete(xfrd->nsd->rc); /* ssl-delete secret keys */ + if (xfrd->nsd->tls_ctx) + SSL_CTX_free(xfrd->nsd->tls_ctx); #endif #ifdef USE_DNSTAP dt_collector_close(nsd.dt_collector, &nsd); @@ -1038,6 +1042,8 @@ xfrd_udp_obtain(xfrd_zone_type* zone) else { if(zone->event_added) event_del(&zone->zone_handler); + memset(&zone->zone_handler, 0, + sizeof(zone->zone_handler)); event_set(&zone->zone_handler, fd, EV_PERSIST|EV_READ|EV_TIMEOUT, xfrd_handle_zone, zone); @@ -1174,6 +1180,7 @@ xfrd_set_timer(xfrd_zone_type* zone, time_t t) else fd = -1; zone->timeout.tv_sec = t; zone->timeout.tv_usec = 0; + memset(&zone->zone_handler, 0, sizeof(zone->zone_handler)); event_set(&zone->zone_handler, fd, fl, xfrd_handle_zone, zone); if(event_base_set(xfrd->event_base, &zone->zone_handler) != 0) log_msg(LOG_ERR, "xfrd timer: event_base_set failed"); @@ -1200,7 +1207,7 @@ xfrd_handle_incoming_soa(xfrd_zone_type* zone, if(zone->soa_disk_acquired && soa->serial == zone->soa_disk.serial) { /* soa in disk has been loaded in memory */ - log_msg(LOG_INFO, "zone %s serial %u is updated to %u.", + log_msg(LOG_INFO, "zone %s serial %u is updated to %u", zone->apex_str, (unsigned)ntohl(zone->soa_nsd.serial), (unsigned)ntohl(soa->serial)); zone->soa_nsd = zone->soa_disk; @@ -1324,6 +1331,8 @@ xfrd_udp_release(xfrd_zone_type* zone) if(fd != -1) { if(wz->event_added) event_del(&wz->zone_handler); + memset(&wz->zone_handler, 0, + sizeof(wz->zone_handler)); event_set(&wz->zone_handler, fd, EV_READ|EV_TIMEOUT|EV_PERSIST, xfrd_handle_zone, wz); @@ -2216,6 +2225,7 @@ xfrd_set_reload_timeout() tv.tv_usec = 0; if(tv.tv_sec > xfrd->nsd->options->xfrd_reload_timeout) tv.tv_sec = xfrd->nsd->options->xfrd_reload_timeout; + memset(&xfrd->reload_handler, 0, sizeof(xfrd->reload_handler)); event_set(&xfrd->reload_handler, -1, EV_TIMEOUT, xfrd_handle_reload, xfrd); if(event_base_set(xfrd->event_base, &xfrd->reload_handler) != 0) @@ -2567,6 +2577,7 @@ static void xfrd_write_timer_set() return; tv.tv_sec = xfrd->nsd->options->zonefiles_write; tv.tv_usec = 0; + memset(&xfrd->write_timer, 0, sizeof(xfrd->write_timer)); event_set(&xfrd->write_timer, -1, EV_TIMEOUT, xfrd_handle_write_timer, xfrd); if(event_base_set(xfrd->event_base, &xfrd->write_timer) != 0) diff --git a/usr.sbin/nsd/zlexer.lex b/usr.sbin/nsd/zlexer.lex index 728f92603f6..14dfbf3c7fd 100644 --- a/usr.sbin/nsd/zlexer.lex +++ b/usr.sbin/nsd/zlexer.lex @@ -58,6 +58,10 @@ push_parser_state(FILE *input) static void pop_parser_state(void) { + if (parser->filename) + region_recycle(parser->region, (void *)parser->filename, + strlen(parser->filename)+1); + --include_stack_ptr; parser->filename = zparser_stack[include_stack_ptr].filename; parser->line = zparser_stack[include_stack_ptr].line; diff --git a/usr.sbin/nsd/zonec.c b/usr.sbin/nsd/zonec.c index 3caec0686f8..e0737968582 100644 --- a/usr.sbin/nsd/zonec.c +++ b/usr.sbin/nsd/zonec.c @@ -1409,8 +1409,10 @@ process_rr(void) assert(zone); if (rr->type == TYPE_SOA) { if (rr->owner != zone->apex) { + char s[MAXDOMAINLEN*5]; + snprintf(s, sizeof(s), "%s", domain_to_string(zone->apex)); zc_error_prev_line( - "SOA record with invalid domain name"); + "SOA record with invalid domain name, '%s' is not '%s'", domain_to_string(rr->owner), s); return 0; } if(has_soa(rr->owner)) { @@ -1425,10 +1427,12 @@ process_rr(void) if (!domain_is_subdomain(rr->owner, zone->apex)) { + char s[MAXDOMAINLEN*5]; + snprintf(s, sizeof(s), "%s", domain_to_string(zone->apex)); if(zone_is_slave(zone->opts)) - zc_warning_prev_line("out of zone data"); + zc_warning_prev_line("out of zone data: %s is outside the zone for fqdn %s", domain_to_string(rr->owner), s); else - zc_error_prev_line("out of zone data"); + zc_error_prev_line("out of zone data: %s is outside the zone for fqdn %s", domain_to_string(rr->owner), s); return 0; } @@ -1464,6 +1468,16 @@ process_rr(void) /* Discard the duplicates... */ if (i < rrset->rr_count) { + /* add rdatas to recycle bin. */ + size_t i; + for (i = 0; i < rr->rdata_count; i++) { + if(!rdata_atom_is_domain(rr->type, i)) + region_recycle(parser->region, rr->rdatas[i].data, + rdata_atom_size(rr->rdatas[i]) + + sizeof(uint16_t)); + } + region_recycle(parser->region, rr->rdatas, + sizeof(rdata_atom_type)*rr->rdata_count); return 0; } if(rrset->rr_count == 65535) { @@ -1732,3 +1746,32 @@ zonec_parse_string(region_type* region, domain_table_type* domains, parser_flush(); return errors; } + +/** check SSHFP type for failures and emit warnings */ +void check_sshfp(void) +{ + uint8_t hash; + uint16_t size; + if(parser->current_rr.rdata_count < 3) + return; /* cannot check it, too few rdata elements */ + if(!parser->current_rr.rdatas[0].data || + !parser->current_rr.rdatas[1].data || + !parser->current_rr.rdatas[2].data || + !parser->current_rr.owner) + return; /* cannot check, NULLs (due to earlier errors) */ + if(rdata_atom_size(parser->current_rr.rdatas[1]) != 1) + return; /* wrong size of the hash type rdata element */ + hash = rdata_atom_data(parser->current_rr.rdatas[1])[0]; + size = rdata_atom_size(parser->current_rr.rdatas[2]); + if(hash == 1 && size != 20) { + zc_warning_prev_line("SSHFP %s of type SHA1 has hash of " + "wrong length, %d bytes, should be 20", + domain_to_string(parser->current_rr.owner), + (int)size); + } else if(hash == 2 && size != 32) { + zc_warning_prev_line("SSHFP %s of type SHA256 has hash of " + "wrong length, %d bytes, should be 32", + domain_to_string(parser->current_rr.owner), + (int)size); + } +} diff --git a/usr.sbin/nsd/zonec.h b/usr.sbin/nsd/zonec.h index b14bedb20ca..66ccfc07bd3 100644 --- a/usr.sbin/nsd/zonec.h +++ b/usr.sbin/nsd/zonec.h @@ -47,7 +47,6 @@ struct zparser { zone_type *current_zone; domain_type *origin; domain_type *prev_dname; - domain_type *default_apex; int error_occurred; unsigned int errors; @@ -143,5 +142,7 @@ unsigned int zonec_read(const char *name, const char *zonefile, zone_type* zone) * The string must end with a newline after the RR. */ int zonec_parse_string(region_type* region, domain_table_type* domains, zone_type* zone, char* str, domain_type** parsed, int* num_rrs); +/** check SSHFP type for failures and emit warnings */ +void check_sshfp(void); #endif /* _ZONEC_H_ */ diff --git a/usr.sbin/nsd/zparser.y b/usr.sbin/nsd/zparser.y index d19387d91a5..0b2e9554f25 100644 --- a/usr.sbin/nsd/zparser.y +++ b/usr.sbin/nsd/zparser.y @@ -633,7 +633,7 @@ type_and_rdata: | T_DLV sp rdata_dlv { if (dlv_warn) { dlv_warn = 0; zc_warning_prev_line("DLV is experimental"); } } | T_DLV sp rdata_unknown { if (dlv_warn) { dlv_warn = 0; zc_warning_prev_line("DLV is experimental"); } $$ = $1; parse_unknown_rdata($1, $3); } | T_SSHFP sp rdata_sshfp - | T_SSHFP sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } + | T_SSHFP sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); check_sshfp(); } | T_RRSIG sp rdata_rrsig | T_RRSIG sp rdata_unknown { $$ = $1; parse_unknown_rdata($1, $3); } | T_NSEC sp rdata_nsec @@ -906,6 +906,7 @@ rdata_sshfp: STR sp STR sp str_sp_seq trail zadd_rdata_wireformat(zparser_conv_byte(parser->region, $1.str)); /* alg */ zadd_rdata_wireformat(zparser_conv_byte(parser->region, $3.str)); /* fp type */ zadd_rdata_wireformat(zparser_conv_hex(parser->region, $5.str, $5.len)); /* hash */ + check_sshfp(); } ; @@ -1020,6 +1021,10 @@ rdata_ipsec_base: STR sp STR sp STR sp dotted_str if(parser->origin == error_domain) { zc_error("cannot concatenate origin to domain name, because origin failed to parse"); break; + } else if(name->name_size + domain_dname(parser->origin)->name_size - 1 > MAXDOMAINLEN) { + zc_error("ipsec gateway name exceeds %d character limit", + MAXDOMAINLEN); + break; } name = dname_concatenate(parser->rr_region, name, domain_dname(parser->origin)); @@ -1157,7 +1162,6 @@ zparser_create(region_type *region, region_type *rr_region, namedb_type *db) result->current_zone = NULL; result->origin = NULL; result->prev_dname = NULL; - result->default_apex = NULL; result->temporary_rdatas = (rdata_atom_type *) region_alloc_array( result->region, MAXRDATALEN, sizeof(rdata_atom_type)); @@ -1181,7 +1185,6 @@ zparser_init(const char *filename, uint32_t ttl, uint16_t klass, parser->current_zone = NULL; parser->origin = domain_table_insert(parser->db->domains, origin); parser->prev_dname = parser->origin; - parser->default_apex = parser->origin; parser->error_occurred = 0; parser->errors = 0; parser->line = 1; |