*prod*
On Thu, Dec 06, 2018 at 11:02:01AM +0100, Florian Obser wrote:
> tests, OKs?
>
> diff --git Makefile.in Makefile.in
> index 16d193f766d..fbfc44be33b 100644
> --- Makefile.in
> +++ Makefile.in
> @@ -29,6 +29,8 @@ nsdconfigfile = @nsd_conf_file@
> zonesdir = @zonesdir@
> chrootdir= @chrootdir@
> user = @user@
> +DNSTAP_SRC=@DNSTAP_SRC@
> +DNSTAP_OBJ=@DNSTAP_OBJ@
>
> # override $U variable which is used by autotools for deansification (for
> # K&R C compilers), but causes problems if $U is defined in the env).
> @@ -47,6 +49,7 @@ INSTALL_DATA = $(INSTALL) -m 644
>
> YACC = @YACC@
> LEX = @LEX@
> +PROTOC_C = @PROTOC_C@
>
> COMPILE = $(CC) $(CPPFLAGS) $(CFLAGS)
> LINK = $(CC) $(CFLAGS) $(LDFLAGS)
> @@ -72,7 +75,7 @@ TARGETS=nsd nsd-checkconf nsd-checkzone nsd-control
> nsd.conf.sample nsd-control-
> MANUALS=nsd.8 nsd-checkconf.8 nsd-checkzone.8 nsd-control.8 nsd.conf.5
>
> COMMON_OBJ=answer.o axfr.o buffer.o configlexer.o configparser.o dname.o
> dns.o edns.o iterated_hash.o lookup3.o namedb.o nsec3.o options.o packet.o
> query.o rbtree.o radtree.o rdata.o region-allocator.o rrl.o tsig.o
> tsig-openssl.o udb.o udbradtree.o udbzone.o util.o
> -XFRD_OBJ=xfrd-disk.o xfrd-notify.o xfrd-tcp.o xfrd.o remote.o
> +XFRD_OBJ=xfrd-disk.o xfrd-notify.o xfrd-tcp.o xfrd.o remote.o $(DNSTAP_OBJ)
> NSD_OBJ=$(COMMON_OBJ) $(XFRD_OBJ) difffile.o ipc.o mini_event.o netio.o
> nsd.o server.o dbaccess.o dbcreate.o zlexer.o zonec.o zparser.o
> ALL_OBJ=$(NSD_OBJ) nsd-checkconf.o nsd-checkzone.o nsd-control.o nsd-mem.o
> NSD_CHECKCONF_OBJ=$(COMMON_OBJ) nsd-checkconf.o
> @@ -306,6 +309,22 @@ configlexer.c: $(srcdir)/configlexer.lex
> configparser.c configparser.h: $(srcdir)/configparser.y
> $(YACC) -d -o configparser.c $(srcdir)/configparser.y
>
> +# dnstap
> +dnstap.o: $(srcdir)/dnstap/dnstap.c config.h \
> + dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.h $(srcdir)/dnstap/dnstap.h \
> + $(srcdir)/util.h $(srcdir)/options.h $(srcdir)/rbtree.h \
> + $(srcdir)/region-allocator.h
> +dnstap.pb-c.o: dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.h
> +dnstap_collector.o: $(srcdir)/dnstap/dnstap_collector.c config.h \
> + $(srcdir)/dnstap/dnstap.h $(srcdir)/dnstap/dnstap_collector.h \
> + $(srcdir)/util.h $(srcdir)/nsd.h $(srcdir)/region-allocator.h \
> + $(srcdir)/buffer.h $(srcdir)/namedb.h $(srcdir)/dname.h \
> + $(srcdir)/dns.h $(srcdir)/radtree.h $(srcdir)/rbtree.h \
> + $(srcdir)/options.h
> +dnstap/dnstap.pb-c.c dnstap/dnstap.pb-c.h: $(srcdir)/dnstap/dnstap.proto
> + @-if test ! -d dnstap; then $(INSTALL) -d dnstap; fi
> + $(PROTOC_C) --c_out=. --proto_path=$(srcdir)
> $(srcdir)/dnstap/dnstap.proto
> +
> # autoconf rules
> config.h.in: configure.ac
> autoheader
> diff --git config.h.in config.h.in
> index 4d47f603062..67296ca99b7 100644
> --- config.h.in
> +++ config.h.in
> @@ -22,6 +22,9 @@
> /* Pathname to the NSD database */
> #undef DBFILE
>
> +/* default dnstap socket path */
> +#undef DNSTAP_SOCKET_PATH
> +
> /* Define to the default maximum message length with EDNS. */
> #undef EDNS_MAX_MESSAGE_LEN
>
> @@ -510,6 +513,9 @@
> /* the user name to drop privileges to */
> #undef USER
>
> +/* Define to 1 to enable dnstap support */
> +#undef USE_DNSTAP
> +
> /* Define if you want to use internal select based events */
> #undef USE_MINI_EVENT
>
> diff --git configlexer.lex configlexer.lex
> index 7fd4f17363f..ead1b96fa80 100644
> --- configlexer.lex
> +++ configlexer.lex
> @@ -117,9 +117,8 @@ static void config_start_include_glob(const char*
> filename)
> #ifdef GLOB_ERR
> | GLOB_ERR
> #endif
> -#ifdef GLOB_NOSORT
> - | GLOB_NOSORT
> -#endif
> + /* do not set GLOB_NOSORT so the results are sorted
> + and in a predictable order. */
> #ifdef GLOB_BRACE
> | GLOB_BRACE
> #endif
> @@ -270,6 +269,15 @@ rrl-whitelist-ratelimit{COLON} { LEXOUT(("v(%s) ",
> yytext)); return VAR_RRL_WHIT
> rrl-whitelist{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_RRL_WHITELIST;}
> zonefiles-check{COLON} { LEXOUT(("v(%s) ", yytext)); return
> VAR_ZONEFILES_CHECK;}
> zonefiles-write{COLON} { LEXOUT(("v(%s) ", yytext)); return
> VAR_ZONEFILES_WRITE;}
> +dnstap{COLON} { LEXOUT(("v(%s) ", yytext)); return
> VAR_DNSTAP;}
> +dnstap-enable{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_DNSTAP_ENABLE;}
> +dnstap-socket-path{COLON} { LEXOUT(("v(%s) ", yytext)); return
> VAR_DNSTAP_SOCKET_PATH; }
> +dnstap-send-identity{COLON} { LEXOUT(("v(%s) ", yytext)); return
> VAR_DNSTAP_SEND_IDENTITY; }
> +dnstap-send-version{COLON} { LEXOUT(("v(%s) ", yytext)); return
> VAR_DNSTAP_SEND_VERSION; }
> +dnstap-identity{COLON} { LEXOUT(("v(%s) ", yytext)); return
> VAR_DNSTAP_IDENTITY; }
> +dnstap-version{COLON} { LEXOUT(("v(%s) ", yytext)); return
> VAR_DNSTAP_VERSION; }
> +dnstap-log-auth-query-messages{COLON} { LEXOUT(("v(%s) ", yytext));
> return VAR_DNSTAP_LOG_AUTH_QUERY_MESSAGES; }
> +dnstap-log-auth-response-messages{COLON} { LEXOUT(("v(%s) ", yytext));
> return VAR_DNSTAP_LOG_AUTH_RESPONSE_MESSAGES; }
> log-time-ascii{COLON} { LEXOUT(("v(%s) ", yytext)); return
> VAR_LOG_TIME_ASCII;}
> round-robin{COLON} { LEXOUT(("v(%s) ", yytext)); return VAR_ROUND_ROBIN;}
> minimal-responses{COLON} { LEXOUT(("v(%s) ", yytext)); return
> VAR_MINIMAL_RESPONSES;}
> diff --git configparser.y configparser.y
> index 567641ce706..1e4d75e9a47 100644
> --- configparser.y
> +++ configparser.y
> @@ -72,13 +72,16 @@ extern config_parser_state_type* cfg_parser;
> %token VAR_MAX_REFRESH_TIME VAR_MIN_REFRESH_TIME
> %token VAR_MAX_RETRY_TIME VAR_MIN_RETRY_TIME
> %token VAR_MULTI_MASTER_CHECK VAR_MINIMAL_RESPONSES VAR_REFUSE_ANY
> -%token VAR_USE_SYSTEMD
> +%token VAR_USE_SYSTEMD VAR_DNSTAP VAR_DNSTAP_ENABLE VAR_DNSTAP_SOCKET_PATH
> +%token VAR_DNSTAP_SEND_IDENTITY VAR_DNSTAP_SEND_VERSION VAR_DNSTAP_IDENTITY
> +%token VAR_DNSTAP_VERSION VAR_DNSTAP_LOG_AUTH_QUERY_MESSAGES
> +%token VAR_DNSTAP_LOG_AUTH_RESPONSE_MESSAGES
>
> %%
> toplevelvars: /* empty */ | toplevelvars toplevelvar ;
> toplevelvar: serverstart contents_server | zonestart contents_zone |
> keystart contents_key | patternstart contents_pattern |
> - rcstart contents_rc;
> + rcstart contents_rc | dtstart contents_dt;
>
> /* server: declaration */
> serverstart: VAR_SERVER
> @@ -596,6 +599,79 @@ rc_control_cert_file: VAR_CONTROL_CERT_FILE STRING
> }
> ;
>
> +/* dnstap: declaration */
> +dtstart: VAR_DNSTAP
> + {
> + OUTYY(("\nP(dnstap:)\n"));
> + }
> + ;
> +contents_dt: contents_dt content_dt
> + | ;
> +content_dt: dt_dnstap_enable | dt_dnstap_socket_path |
> + dt_dnstap_send_identity | dt_dnstap_send_version |
> + dt_dnstap_identity | dt_dnstap_version |
> + dt_dnstap_log_auth_query_messages |
> + dt_dnstap_log_auth_response_messages
> + ;
> +dt_dnstap_enable: VAR_DNSTAP_ENABLE STRING
> + {
> + OUTYY(("P(dt_dnstap_enable:%s)\n", $2));
> + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
> + yyerror("expected yes or no.");
> + else cfg_parser->opt->dnstap_enable = (strcmp($2, "yes")==0);
> + }
> + ;
> +dt_dnstap_socket_path: VAR_DNSTAP_SOCKET_PATH STRING
> + {
> + OUTYY(("P(dt_dnstap_socket_path:%s)\n", $2));
> + cfg_parser->opt->dnstap_socket_path =
> region_strdup(cfg_parser->opt->region, $2);
> + }
> + ;
> +dt_dnstap_send_identity: VAR_DNSTAP_SEND_IDENTITY STRING
> + {
> + OUTYY(("P(dt_dnstap_send_identity:%s)\n", $2));
> + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
> + yyerror("expected yes or no.");
> + else cfg_parser->opt->dnstap_send_identity = (strcmp($2,
> "yes")==0);
> + }
> + ;
> +dt_dnstap_send_version: VAR_DNSTAP_SEND_VERSION STRING
> + {
> + OUTYY(("P(dt_dnstap_send_version:%s)\n", $2));
> + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
> + yyerror("expected yes or no.");
> + else cfg_parser->opt->dnstap_send_version = (strcmp($2,
> "yes")==0);
> + }
> + ;
> +dt_dnstap_identity: VAR_DNSTAP_IDENTITY STRING
> + {
> + OUTYY(("P(dt_dnstap_identity:%s)\n", $2));
> + cfg_parser->opt->dnstap_identity =
> region_strdup(cfg_parser->opt->region, $2);
> + }
> + ;
> +dt_dnstap_version: VAR_DNSTAP_VERSION STRING
> + {
> + OUTYY(("P(dt_dnstap_version:%s)\n", $2));
> + cfg_parser->opt->dnstap_version =
> region_strdup(cfg_parser->opt->region, $2);
> + }
> + ;
> +dt_dnstap_log_auth_query_messages: VAR_DNSTAP_LOG_AUTH_QUERY_MESSAGES STRING
> + {
> + OUTYY(("P(dt_dnstap_log_auth_query_messages:%s)\n", $2));
> + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
> + yyerror("expected yes or no.");
> + else cfg_parser->opt->dnstap_log_auth_query_messages =
> (strcmp($2, "yes")==0);
> + }
> + ;
> +dt_dnstap_log_auth_response_messages: VAR_DNSTAP_LOG_AUTH_RESPONSE_MESSAGES
> STRING
> + {
> + OUTYY(("P(dt_dnstap_log_auth_response_messages:%s)\n", $2));
> + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
> + yyerror("expected yes or no.");
> + else cfg_parser->opt->dnstap_log_auth_response_messages =
> (strcmp($2, "yes")==0);
> + }
> + ;
> +
> /* pattern: declaration */
> patternstart: VAR_PATTERN
> {
> diff --git configure configure
> index a4b87938db6..47736231022 100644
> --- configure
> +++ 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.25.
> +# Generated by GNU Autoconf 2.69 for NSD 4.1.26.
> #
> # Report bugs to <[email protected]>.
> #
> @@ -580,8 +580,8 @@ MAKEFLAGS=
> # Identity of this package.
> PACKAGE_NAME='NSD'
> PACKAGE_TARNAME='nsd'
> -PACKAGE_VERSION='4.1.25'
> -PACKAGE_STRING='NSD 4.1.25'
> +PACKAGE_VERSION='4.1.26'
> +PACKAGE_STRING='NSD 4.1.26'
> PACKAGE_BUGREPORT='[email protected]'
> PACKAGE_URL=''
>
> @@ -622,6 +622,11 @@ ac_includes_default="\
> #endif"
>
> ac_subst_vars='LTLIBOBJS
> +DNSTAP_OBJ
> +DNSTAP_SRC
> +opt_dnstap_socket_path
> +ENABLE_DNSTAP
> +PROTOC_C
> SSL_LIBS
> HAVE_SSL
> ratelimit_default
> @@ -734,6 +739,10 @@ enable_minimal_responses
> enable_mmap
> enable_radix_tree
> enable_packed
> +enable_dnstap
> +with_dnstap_socket_path
> +with_protobuf_c
> +with_libfstrm
> enable_systemd
> '
> ac_precious_vars='build_alias
> @@ -1287,7 +1296,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.25 to adapt to many kinds of systems.
> +\`configure' configures NSD 4.1.26 to adapt to many kinds of systems.
>
> Usage: $0 [OPTION]... [VAR=VALUE]...
>
> @@ -1348,7 +1357,7 @@ fi
>
> if test -n "$ac_init_help"; then
> case $ac_init_help in
> - short | recursive ) echo "Configuration of NSD 4.1.25:";;
> + short | recursive ) echo "Configuration of NSD 4.1.26:";;
> esac
> cat <<\_ACEOF
>
> @@ -1387,6 +1396,7 @@ Optional Features:
> less memory, but uses some more CPU.
> --enable-packed Enable packed structure alignment, uses less
> memory,
> but unaligned reads.
> + --enable-dnstap Enable dnstap support (requires fstrm, protobuf-c)
> --enable-systemd compile with systemd support
>
> Optional Packages:
> @@ -1415,6 +1425,10 @@ Optional Packages:
> Limit the default tcp timeout
> --with-ssl=pathname enable SSL (will check /usr/local/ssl /usr/lib/ssl
> /usr/ssl /usr/pkg /usr/sfw /usr/local /usr)
> + --with-dnstap-socket-path=pathname
> + set default dnstap socket path
> + --with-protobuf-c=path Path where protobuf-c is installed, for dnstap
> + --with-libfstrm=path Path where libfstrm is installed, for dnstap
>
> Some influential environment variables:
> CC C compiler command
> @@ -1498,7 +1512,7 @@ fi
> test -n "$ac_init_help" && exit $ac_status
> if $ac_init_version; then
> cat <<\_ACEOF
> -NSD configure 4.1.25
> +NSD configure 4.1.26
> generated by GNU Autoconf 2.69
>
> Copyright (C) 2012 Free Software Foundation, Inc.
> @@ -2207,7 +2221,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.25, which was
> +It was created by NSD $as_me 4.1.26, which was
> generated by GNU Autoconf 2.69. Invocation command line was
>
> $ $0 $@
> @@ -9300,6 +9314,251 @@ fi
> ;;
> esac
>
> +# check for dnstap if requested
> +
> + # Check whether --enable-dnstap was given.
> +if test "${enable_dnstap+set}" = set; then :
> + enableval=$enable_dnstap; opt_dnstap=$enableval
> +else
> + opt_dnstap=no
> +fi
> +
> +
> +
> +# Check whether --with-dnstap-socket-path was given.
> +if test "${with_dnstap_socket_path+set}" = set; then :
> + withval=$with_dnstap_socket_path; opt_dnstap_socket_path=$withval
> +else
> + opt_dnstap_socket_path="${localstatedir}/run/nsd-dnstap.sock"
> +fi
> +
> +
> + if test "x$opt_dnstap" != "xno"; then
> + # Extract the first word of "protoc-c", so it can be a program name with
> args.
> +set dummy protoc-c; ac_word=$2
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
> +$as_echo_n "checking for $ac_word... " >&6; }
> +if ${ac_cv_path_PROTOC_C+:} false; then :
> + $as_echo_n "(cached) " >&6
> +else
> + case $PROTOC_C in
> + [\\/]* | ?:[\\/]*)
> + ac_cv_path_PROTOC_C="$PROTOC_C" # Let the user override the test with a
> path.
> + ;;
> + *)
> + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
> +for as_dir in $PATH
> +do
> + IFS=$as_save_IFS
> + test -z "$as_dir" && as_dir=.
> + for ac_exec_ext in '' $ac_executable_extensions; do
> + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
> + ac_cv_path_PROTOC_C="$as_dir/$ac_word$ac_exec_ext"
> + $as_echo "$as_me:${as_lineno-$LINENO}: found
> $as_dir/$ac_word$ac_exec_ext" >&5
> + break 2
> + fi
> +done
> + done
> +IFS=$as_save_IFS
> +
> + ;;
> +esac
> +fi
> +PROTOC_C=$ac_cv_path_PROTOC_C
> +if test -n "$PROTOC_C"; then
> + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROTOC_C" >&5
> +$as_echo "$PROTOC_C" >&6; }
> +else
> + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
> +$as_echo "no" >&6; }
> +fi
> +
> +
> + if test -z "$PROTOC_C"; then
> + as_fn_error $? "The protoc-c program was not found. Please install
> protobuf-c!" "$LINENO" 5
> + fi
> +
> +# Check whether --with-protobuf-c was given.
> +if test "${with_protobuf_c+set}" = set; then :
> + withval=$with_protobuf_c;
> + # workaround for protobuf-c includes at old dir before
> protobuf-c-1.0.0
> + if test -f $withval/include/google/protobuf-c/protobuf-c.h; then
> + CFLAGS="$CFLAGS -I$withval/include/google"
> + else
> + CFLAGS="$CFLAGS -I$withval/include"
> + fi
> + LDFLAGS="$LDFLAGS -L$withval/lib"
> +
> +else
> +
> + # workaround for protobuf-c includes at old dir before
> protobuf-c-1.0.0
> + if test -f /usr/include/google/protobuf-c/protobuf-c.h; then
> + CFLAGS="$CFLAGS -I/usr/include/google"
> + else
> + if test -f /usr/local/include/google/protobuf-c/protobuf-c.h; then
> + CFLAGS="$CFLAGS -I/usr/local/include/google"
> + LDFLAGS="$LDFLAGS -L/usr/local/lib"
> + fi
> + fi
> +
> +fi
> +
> +
> +# Check whether --with-libfstrm was given.
> +if test "${with_libfstrm+set}" = set; then :
> + withval=$with_libfstrm;
> + CFLAGS="$CFLAGS -I$withval/include"
> + LDFLAGS="$LDFLAGS -L$withval/lib"
> +
> +fi
> +
> + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing
> fstrm_iothr_init" >&5
> +$as_echo_n "checking for library containing fstrm_iothr_init... " >&6; }
> +if ${ac_cv_search_fstrm_iothr_init+:} false; then :
> + $as_echo_n "(cached) " >&6
> +else
> + ac_func_search_save_LIBS=$LIBS
> +cat confdefs.h - <<_ACEOF >conftest.$ac_ext
> +/* end confdefs.h. */
> +
> +/* Override any GCC internal prototype to avoid an error.
> + Use char because int might match the return type of a GCC
> + builtin and then its argument prototype would still apply. */
> +#ifdef __cplusplus
> +extern "C"
> +#endif
> +char fstrm_iothr_init ();
> +int
> +main ()
> +{
> +return fstrm_iothr_init ();
> + ;
> + return 0;
> +}
> +_ACEOF
> +for ac_lib in '' fstrm; do
> + if test -z "$ac_lib"; then
> + ac_res="none required"
> + else
> + ac_res=-l$ac_lib
> + LIBS="-l$ac_lib $ac_func_search_save_LIBS"
> + fi
> + if ac_fn_c_try_link "$LINENO"; then :
> + ac_cv_search_fstrm_iothr_init=$ac_res
> +fi
> +rm -f core conftest.err conftest.$ac_objext \
> + conftest$ac_exeext
> + if ${ac_cv_search_fstrm_iothr_init+:} false; then :
> + break
> +fi
> +done
> +if ${ac_cv_search_fstrm_iothr_init+:} false; then :
> +
> +else
> + ac_cv_search_fstrm_iothr_init=no
> +fi
> +rm conftest.$ac_ext
> +LIBS=$ac_func_search_save_LIBS
> +fi
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: result:
> $ac_cv_search_fstrm_iothr_init" >&5
> +$as_echo "$ac_cv_search_fstrm_iothr_init" >&6; }
> +ac_res=$ac_cv_search_fstrm_iothr_init
> +if test "$ac_res" != no; then :
> + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
> +
> +else
> + as_fn_error $? "The fstrm library was not found. Please install fstrm!"
> "$LINENO" 5
> +fi
> +
> + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing
> protobuf_c_message_pack" >&5
> +$as_echo_n "checking for library containing protobuf_c_message_pack... "
> >&6; }
> +if ${ac_cv_search_protobuf_c_message_pack+:} false; then :
> + $as_echo_n "(cached) " >&6
> +else
> + ac_func_search_save_LIBS=$LIBS
> +cat confdefs.h - <<_ACEOF >conftest.$ac_ext
> +/* end confdefs.h. */
> +
> +/* Override any GCC internal prototype to avoid an error.
> + Use char because int might match the return type of a GCC
> + builtin and then its argument prototype would still apply. */
> +#ifdef __cplusplus
> +extern "C"
> +#endif
> +char protobuf_c_message_pack ();
> +int
> +main ()
> +{
> +return protobuf_c_message_pack ();
> + ;
> + return 0;
> +}
> +_ACEOF
> +for ac_lib in '' protobuf-c; do
> + if test -z "$ac_lib"; then
> + ac_res="none required"
> + else
> + ac_res=-l$ac_lib
> + LIBS="-l$ac_lib $ac_func_search_save_LIBS"
> + fi
> + if ac_fn_c_try_link "$LINENO"; then :
> + ac_cv_search_protobuf_c_message_pack=$ac_res
> +fi
> +rm -f core conftest.err conftest.$ac_objext \
> + conftest$ac_exeext
> + if ${ac_cv_search_protobuf_c_message_pack+:} false; then :
> + break
> +fi
> +done
> +if ${ac_cv_search_protobuf_c_message_pack+:} false; then :
> +
> +else
> + ac_cv_search_protobuf_c_message_pack=no
> +fi
> +rm conftest.$ac_ext
> +LIBS=$ac_func_search_save_LIBS
> +fi
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: result:
> $ac_cv_search_protobuf_c_message_pack" >&5
> +$as_echo "$ac_cv_search_protobuf_c_message_pack" >&6; }
> +ac_res=$ac_cv_search_protobuf_c_message_pack
> +if test "$ac_res" != no; then :
> + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
> +
> +else
> + as_fn_error $? "The protobuf-c library was not found. Please install
> protobuf-c!" "$LINENO" 5
> +fi
> +
> +
> +
> +$as_echo "#define USE_DNSTAP 1" >>confdefs.h
> +
> + ENABLE_DNSTAP=1
> +
> +
> +
> + hdr_dnstap_socket_path="`echo $opt_dnstap_socket_path | sed -e
> 's/\\\\/\\\\\\\\/g'`"
> +
> +
> +cat >>confdefs.h <<_ACEOF
> +#define DNSTAP_SOCKET_PATH "$hdr_dnstap_socket_path"
> +_ACEOF
> +
> +
> + DNSTAP_SRC="dnstap/dnstap.c dnstap/dnstap.pb-c.c
> dnstap/dnstap_collector.c"
> +
> + DNSTAP_OBJ="dnstap.o dnstap_collector.o dnstap.pb-c.o"
> +
> + dnstap_config="dnstap/dnstap_config.h"
> +
> + else
> +
> + ENABLE_DNSTAP=0
> +
> +
> +
> + fi
> +
> +
> # Include systemd.m4 - begin
> # macros for configuring systemd
> # Copyright 2015, Sami Kerola, CloudFlare.
> @@ -9360,7 +9619,7 @@ if test "$enable_checking" = "yes"; then
> echo "************************************************"
> fi
>
> -ac_config_files="$ac_config_files Makefile"
> +ac_config_files="$ac_config_files Makefile $dnstap_config"
>
> cat >confcache <<\_ACEOF
> # This file is a shell script that caches the results of configure
> @@ -9868,7 +10127,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.25, which was
> +This file was extended by NSD $as_me 4.1.26, which was
> generated by GNU Autoconf 2.69. Invocation command line was
>
> CONFIG_FILES = $CONFIG_FILES
> @@ -9930,7 +10189,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.25
> +NSD config.status 4.1.26
> configured by $0, generated by GNU Autoconf 2.69,
> with options \\"\$ac_cs_config\\"
>
> @@ -10055,6 +10314,7 @@ do
> case $ac_config_target in
> "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
> "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
> + "$dnstap_config") CONFIG_FILES="$CONFIG_FILES $dnstap_config" ;;
>
> *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
> esac
> diff --git configure.ac configure.ac
> index ad5399a9d72..22e3e21e2ae 100644
> --- configure.ac
> +++ configure.ac
> @@ -3,8 +3,9 @@ dnl Some global settings
> dnl
>
> sinclude(acx_nlnetlabs.m4)
> +sinclude(dnstap/dnstap.m4)
>
> -AC_INIT(NSD,4.1.25,[email protected])
> +AC_INIT(NSD,4.1.26,[email protected])
> AC_CONFIG_HEADER([config.h])
>
> CFLAGS="$CFLAGS"
> @@ -961,6 +962,26 @@ case "$enable_packed" in
> ;;
> esac
>
> +# check for dnstap if requested
> +dt_DNSTAP([${localstatedir}/run/nsd-dnstap.sock],
> + [
> + AC_DEFINE([USE_DNSTAP], [1], [Define to 1 to enable dnstap support])
> + AC_SUBST([ENABLE_DNSTAP], [1])
> +
> + AC_SUBST([opt_dnstap_socket_path])
> + ACX_ESCAPE_BACKSLASH($opt_dnstap_socket_path, hdr_dnstap_socket_path)
> + AC_DEFINE_UNQUOTED(DNSTAP_SOCKET_PATH,
> + ["$hdr_dnstap_socket_path"], [default dnstap socket path])
> +
> + AC_SUBST([DNSTAP_SRC], ["dnstap/dnstap.c dnstap/dnstap.pb-c.c
> dnstap/dnstap_collector.c"])
> + AC_SUBST([DNSTAP_OBJ], ["dnstap.o dnstap_collector.o dnstap.pb-c.o"])
> + dnstap_config="dnstap/dnstap_config.h"
> + ],
> + [
> + AC_SUBST([ENABLE_DNSTAP], [0])
> + ]
> +)
> +
> # Include systemd.m4 - begin
> sinclude(systemd.m4)
> # Include systemd.m4 - end
> @@ -1163,5 +1184,5 @@ if test "$enable_checking" = "yes"; then
> echo "************************************************"
> fi
>
> -AC_CONFIG_FILES([Makefile])
> +AC_CONFIG_FILES([Makefile $dnstap_config])
> AC_OUTPUT
> diff --git dnstap/dnstap.c dnstap/dnstap.c
> new file mode 100644
> index 00000000000..fb724a8fc7c
> --- /dev/null
> +++ dnstap/dnstap.c
> @@ -0,0 +1,433 @@
> +/* dnstap support for NSD */
> +
> +/*
> + * Copyright (c) 2013-2014, Farsight Security, Inc.
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + *
> + * 1. Redistributions of source code must retain the above copyright
> + * notice, this list of conditions and the following disclaimer.
> + *
> + * 2. Redistributions in binary form must reproduce the above copyright
> + * notice, this list of conditions and the following disclaimer in the
> + * documentation and/or other materials provided with the distribution.
> + *
> + * 3. Neither the name of the copyright holder nor the names of its
> + * contributors may be used to endorse or promote products derived from
> + * this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
> + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
> + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
> + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
> + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
> + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
> PROFITS;
> + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
> + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
> + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
> + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include "dnstap/dnstap_config.h"
> +
> +#ifdef USE_DNSTAP
> +
> +#include "config.h"
> +#include <string.h>
> +#include <sys/time.h>
> +#ifdef HAVE_SYS_STAT_H
> +#include <sys/stat.h>
> +#endif
> +#include <errno.h>
> +#include <unistd.h>
> +#include "util.h"
> +#include "options.h"
> +
> +#include <fstrm.h>
> +#include <protobuf-c/protobuf-c.h>
> +
> +#include "dnstap/dnstap.h"
> +#include "dnstap/dnstap.pb-c.h"
> +
> +#define DNSTAP_CONTENT_TYPE "protobuf:dnstap.Dnstap"
> +#define DNSTAP_INITIAL_BUF_SIZE 256
> +
> +struct dt_msg {
> + void *buf;
> + size_t len_buf;
> + Dnstap__Dnstap d;
> + Dnstap__Message m;
> +};
> +
> +static int
> +dt_pack(const Dnstap__Dnstap *d, void **buf, size_t *sz)
> +{
> + ProtobufCBufferSimple sbuf;
> +
> + memset(&sbuf, 0, sizeof(sbuf));
> + sbuf.base.append = protobuf_c_buffer_simple_append;
> + sbuf.len = 0;
> + sbuf.alloced = DNSTAP_INITIAL_BUF_SIZE;
> + sbuf.data = malloc(sbuf.alloced);
> + if (sbuf.data == NULL)
> + return 0;
> + sbuf.must_free_data = 1;
> +
> + *sz = dnstap__dnstap__pack_to_buffer(d, (ProtobufCBuffer *) &sbuf);
> + if (sbuf.data == NULL)
> + return 0;
> + *buf = sbuf.data;
> +
> + return 1;
> +}
> +
> +static void
> +dt_send(const struct dt_env *env, void *buf, size_t len_buf)
> +{
> + fstrm_res res;
> + if (!buf)
> + return;
> + res = fstrm_iothr_submit(env->iothr, env->ioq, buf, len_buf,
> + fstrm_free_wrapper, NULL);
> + if (res != fstrm_res_success)
> + free(buf);
> +}
> +
> +static void
> +dt_msg_init(const struct dt_env *env,
> + struct dt_msg *dm,
> + Dnstap__Message__Type mtype)
> +{
> + memset(dm, 0, sizeof(*dm));
> + dm->d.base.descriptor = &dnstap__dnstap__descriptor;
> + dm->m.base.descriptor = &dnstap__message__descriptor;
> + dm->d.type = DNSTAP__DNSTAP__TYPE__MESSAGE;
> + dm->d.message = &dm->m;
> + dm->m.type = mtype;
> + if (env->identity != NULL) {
> + dm->d.identity.data = (uint8_t *) env->identity;
> + dm->d.identity.len = (size_t) env->len_identity;
> + dm->d.has_identity = 1;
> + }
> + if (env->version != NULL) {
> + dm->d.version.data = (uint8_t *) env->version;
> + dm->d.version.len = (size_t) env->len_version;
> + dm->d.has_version = 1;
> + }
> +}
> +
> +/* check that the socket file can be opened and exists, print error if not */
> +static void
> +check_socket_file(const char* socket_path)
> +{
> + struct stat statbuf;
> + memset(&statbuf, 0, sizeof(statbuf));
> + if(stat(socket_path, &statbuf) < 0) {
> + log_msg(LOG_WARNING, "could not open dnstap-socket-path: %s,
> %s",
> + socket_path, strerror(errno));
> + }
> +}
> +
> +struct dt_env *
> +dt_create(const char *socket_path, unsigned num_workers)
> +{
> +#ifndef NDEBUG
> + fstrm_res res;
> +#endif
> + struct dt_env *env;
> + struct fstrm_iothr_options *fopt;
> + struct fstrm_unix_writer_options *fuwopt;
> + struct fstrm_writer *fw;
> + struct fstrm_writer_options *fwopt;
> +
> + VERBOSITY(1, (LOG_INFO, "attempting to connect to dnstap socket %s",
> + socket_path));
> + assert(socket_path != NULL);
> + assert(num_workers > 0);
> + check_socket_file(socket_path);
> +
> + env = (struct dt_env *) calloc(1, sizeof(struct dt_env));
> + if (!env)
> + return NULL;
> +
> + fwopt = fstrm_writer_options_init();
> +#ifndef NDEBUG
> + res =
> +#else
> + (void)
> +#endif
> + fstrm_writer_options_add_content_type(fwopt,
> + DNSTAP_CONTENT_TYPE, sizeof(DNSTAP_CONTENT_TYPE) - 1);
> + assert(res == fstrm_res_success);
> +
> + fuwopt = fstrm_unix_writer_options_init();
> + fstrm_unix_writer_options_set_socket_path(fuwopt, socket_path);
> +
> + fw = fstrm_unix_writer_init(fuwopt, fwopt);
> + assert(fw != NULL);
> +
> + fopt = fstrm_iothr_options_init();
> + fstrm_iothr_options_set_num_input_queues(fopt, num_workers);
> + env->iothr = fstrm_iothr_init(fopt, &fw);
> + if (env->iothr == NULL) {
> + log_msg(LOG_ERR, "dt_create: fstrm_iothr_init() failed");
> + fstrm_writer_destroy(&fw);
> + free(env);
> + env = NULL;
> + }
> + fstrm_iothr_options_destroy(&fopt);
> + fstrm_unix_writer_options_destroy(&fuwopt);
> + fstrm_writer_options_destroy(&fwopt);
> +
> + return env;
> +}
> +
> +static void
> +dt_apply_identity(struct dt_env *env, struct nsd_options *cfg)
> +{
> + char buf[MAXHOSTNAMELEN+1];
> + if (!cfg->dnstap_send_identity)
> + return;
> + free(env->identity);
> + if (cfg->dnstap_identity == NULL || cfg->dnstap_identity[0] == 0) {
> + if (gethostname(buf, MAXHOSTNAMELEN) == 0) {
> + buf[MAXHOSTNAMELEN] = 0;
> + env->identity = strdup(buf);
> + } else {
> + error("dt_apply_identity: gethostname() failed");
> + }
> + } else {
> + env->identity = strdup(cfg->dnstap_identity);
> + }
> + if (env->identity == NULL)
> + error("dt_apply_identity: strdup() failed");
> + env->len_identity = (unsigned int)strlen(env->identity);
> + VERBOSITY(1, (LOG_INFO, "dnstap identity field set to \"%s\"",
> + env->identity));
> +}
> +
> +static void
> +dt_apply_version(struct dt_env *env, struct nsd_options *cfg)
> +{
> + if (!cfg->dnstap_send_version)
> + return;
> + free(env->version);
> + if (cfg->dnstap_version == NULL || cfg->dnstap_version[0] == 0)
> + env->version = strdup(PACKAGE_STRING);
> + else
> + env->version = strdup(cfg->dnstap_version);
> + if (env->version == NULL)
> + error("dt_apply_version: strdup() failed");
> + env->len_version = (unsigned int)strlen(env->version);
> + VERBOSITY(1, (LOG_INFO, "dnstap version field set to \"%s\"",
> + env->version));
> +}
> +
> +void
> +dt_apply_cfg(struct dt_env *env, struct nsd_options *cfg)
> +{
> + if (!cfg->dnstap_enable)
> + return;
> +
> + dt_apply_identity(env, cfg);
> + dt_apply_version(env, cfg);
> + if ((env->log_auth_query_messages = (unsigned int)
> + cfg->dnstap_log_auth_query_messages))
> + {
> + VERBOSITY(1, (LOG_INFO, "dnstap Message/AUTH_QUERY enabled"));
> + }
> + if ((env->log_auth_response_messages = (unsigned int)
> + cfg->dnstap_log_auth_response_messages))
> + {
> + VERBOSITY(1, (LOG_INFO, "dnstap Message/AUTH_RESPONSE
> enabled"));
> + }
> +}
> +
> +int
> +dt_init(struct dt_env *env)
> +{
> + env->ioq = fstrm_iothr_get_input_queue(env->iothr);
> + if (env->ioq == NULL)
> + return 0;
> + return 1;
> +}
> +
> +void
> +dt_delete(struct dt_env *env)
> +{
> + if (!env)
> + return;
> + VERBOSITY(1, (LOG_INFO, "closing dnstap socket"));
> + fstrm_iothr_destroy(&env->iothr);
> + free(env->identity);
> + free(env->version);
> + free(env);
> +}
> +
> +static void
> +dt_fill_timeval(const struct timeval *tv,
> + uint64_t *time_sec, protobuf_c_boolean *has_time_sec,
> + uint32_t *time_nsec, protobuf_c_boolean *has_time_nsec)
> +{
> +#ifndef S_SPLINT_S
> + *time_sec = tv->tv_sec;
> + *time_nsec = tv->tv_usec * 1000;
> +#endif
> + *has_time_sec = 1;
> + *has_time_nsec = 1;
> +}
> +
> +static void
> +dt_fill_buffer(uint8_t* pkt, size_t pktlen, ProtobufCBinaryData *p,
> protobuf_c_boolean *has)
> +{
> + p->len = pktlen;
> + p->data = pkt;
> + *has = 1;
> +}
> +
> +static void
> +dt_msg_fill_net(struct dt_msg *dm,
> +#ifdef INET6
> + struct sockaddr_storage *ss,
> +#else
> + struct sockaddr_in *ss,
> +#endif
> + int is_tcp,
> + ProtobufCBinaryData *addr, protobuf_c_boolean *has_addr,
> + uint32_t *port, protobuf_c_boolean *has_port)
> +{
> +#ifdef INET6
> + assert(ss->ss_family == AF_INET6 || ss->ss_family == AF_INET);
> + if (ss->ss_family == AF_INET6) {
> + struct sockaddr_in6 *s = (struct sockaddr_in6 *) ss;
> +
> + /* socket_family */
> + dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET6;
> + dm->m.has_socket_family = 1;
> +
> + /* addr: query_address or response_address */
> + addr->data = s->sin6_addr.s6_addr;
> + addr->len = 16; /* IPv6 */
> + *has_addr = 1;
> +
> + /* port: query_port or response_port */
> + *port = ntohs(s->sin6_port);
> + *has_port = 1;
> + } else if (ss->ss_family == AF_INET) {
> +#else
> + if (ss->ss_family == AF_INET) {
> +#endif /* INET6 */
> + struct sockaddr_in *s = (struct sockaddr_in *) ss;
> +
> + /* socket_family */
> + dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET;
> + dm->m.has_socket_family = 1;
> +
> + /* addr: query_address or response_address */
> + addr->data = (uint8_t *) &s->sin_addr.s_addr;
> + addr->len = 4; /* IPv4 */
> + *has_addr = 1;
> +
> + /* port: query_port or response_port */
> + *port = ntohs(s->sin_port);
> + *has_port = 1;
> + }
> +
> + if (!is_tcp) {
> + /* socket_protocol */
> + dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__UDP;
> + dm->m.has_socket_protocol = 1;
> + } else {
> + /* socket_protocol */
> + dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__TCP;
> + dm->m.has_socket_protocol = 1;
> + }
> +}
> +
> +void
> +dt_msg_send_auth_query(struct dt_env *env,
> +#ifdef INET6
> + struct sockaddr_storage* addr,
> +#else
> + struct sockaddr_in* addr,
> +#endif
> + int is_tcp, uint8_t* zone, size_t zonelen, uint8_t* pkt, size_t pktlen)
> +{
> + struct dt_msg dm;
> + struct timeval qtime;
> +
> + gettimeofday(&qtime, NULL);
> +
> + /* type */
> + dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__AUTH_QUERY);
> +
> + if(zone) {
> + /* query_zone */
> + dm.m.query_zone.data = zone;
> + dm.m.query_zone.len = zonelen;
> + dm.m.has_query_zone = 1;
> + }
> +
> + /* query_time */
> + dt_fill_timeval(&qtime,
> + &dm.m.query_time_sec, &dm.m.has_query_time_sec,
> + &dm.m.query_time_nsec, &dm.m.has_query_time_nsec);
> +
> + /* query_message */
> + dt_fill_buffer(pkt, pktlen, &dm.m.query_message,
> &dm.m.has_query_message);
> +
> + /* socket_family, socket_protocol, query_address, query_port */
> + dt_msg_fill_net(&dm, addr, is_tcp,
> + &dm.m.query_address, &dm.m.has_query_address,
> + &dm.m.query_port, &dm.m.has_query_port);
> +
> + if (dt_pack(&dm.d, &dm.buf, &dm.len_buf))
> + dt_send(env, dm.buf, dm.len_buf);
> +}
> +
> +void
> +dt_msg_send_auth_response(struct dt_env *env,
> +#ifdef INET6
> + struct sockaddr_storage* addr,
> +#else
> + struct sockaddr_in* addr,
> +#endif
> + int is_tcp, uint8_t* zone, size_t zonelen, uint8_t* pkt, size_t pktlen)
> +{
> + struct dt_msg dm;
> + struct timeval rtime;
> +
> + gettimeofday(&rtime, NULL);
> +
> + /* type */
> + dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE);
> +
> + if(zone) {
> + /* query_zone */
> + dm.m.query_zone.data = zone;
> + dm.m.query_zone.len = zonelen;
> + dm.m.has_query_zone = 1;
> + }
> +
> + /* response_time */
> + dt_fill_timeval(&rtime,
> + &dm.m.response_time_sec, &dm.m.has_response_time_sec,
> + &dm.m.response_time_nsec, &dm.m.has_response_time_nsec);
> +
> + /* response_message */
> + dt_fill_buffer(pkt, pktlen, &dm.m.response_message,
> &dm.m.has_response_message);
> +
> + /* socket_family, socket_protocol, query_address, query_port */
> + dt_msg_fill_net(&dm, addr, is_tcp,
> + &dm.m.query_address, &dm.m.has_query_address,
> + &dm.m.query_port, &dm.m.has_query_port);
> +
> + if (dt_pack(&dm.d, &dm.buf, &dm.len_buf))
> + dt_send(env, dm.buf, dm.len_buf);
> +}
> +
> +#endif /* USE_DNSTAP */
> diff --git dnstap/dnstap.h dnstap/dnstap.h
> new file mode 100644
> index 00000000000..05b1bd049f3
> --- /dev/null
> +++ dnstap/dnstap.h
> @@ -0,0 +1,148 @@
> +/* dnstap support for NSD */
> +
> +/*
> + * Copyright (c) 2013-2014, Farsight Security, Inc.
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + *
> + * 1. Redistributions of source code must retain the above copyright
> + * notice, this list of conditions and the following disclaimer.
> + *
> + * 2. Redistributions in binary form must reproduce the above copyright
> + * notice, this list of conditions and the following disclaimer in the
> + * documentation and/or other materials provided with the distribution.
> + *
> + * 3. Neither the name of the copyright holder nor the names of its
> + * contributors may be used to endorse or promote products derived from
> + * this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
> + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
> + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
> + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
> + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
> + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
> PROFITS;
> + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
> + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
> + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
> + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef NSD_DNSTAP_H
> +#define NSD_DNSTAP_H
> +
> +#include "dnstap/dnstap_config.h"
> +
> +#ifdef USE_DNSTAP
> +
> +struct nsd_options;
> +struct fstrm_io;
> +struct fstrm_queue;
> +
> +struct dt_env {
> + /** dnstap I/O thread */
> + struct fstrm_iothr *iothr;
> +
> + /** dnstap I/O thread input queue */
> + struct fstrm_iothr_queue *ioq;
> +
> + /** dnstap "identity" field, NULL if disabled */
> + char *identity;
> +
> + /** dnstap "version" field, NULL if disabled */
> + char *version;
> +
> + /** length of "identity" field */
> + unsigned len_identity;
> +
> + /** length of "version" field */
> + unsigned len_version;
> +
> + /** whether to log Message/AUTH_QUERY */
> + unsigned log_auth_query_messages : 1;
> + /** whether to log Message/AUTH_RESPONSE */
> + unsigned log_auth_response_messages : 1;
> +};
> +
> +/**
> + * Create dnstap environment object. Afterwards, call dt_apply_cfg() to fill
> in
> + * the config variables and dt_init() to fill in the per-worker state. Each
> + * worker needs a copy of this object but with its own I/O queue (the fq
> field
> + * of the structure) to ensure lock-free access to its own per-worker
> circular
> + * queue. Duplicate the environment object if more than one worker needs to
> + * share access to the dnstap I/O socket.
> + * @param socket_path: path to dnstap logging socket, must be non-NULL.
> + * @param num_workers: number of worker threads, must be > 0.
> + * @return dt_env object, NULL on failure.
> + */
> +struct dt_env *
> +dt_create(const char *socket_path, unsigned num_workers);
> +
> +/**
> + * Apply config settings.
> + * @param env: dnstap environment object.
> + * @param cfg: new config settings.
> + */
> +void
> +dt_apply_cfg(struct dt_env *env, struct nsd_options *cfg);
> +
> +/**
> + * Initialize per-worker state in dnstap environment object.
> + * @param env: dnstap environment object to initialize, created with
> dt_create().
> + * @return: true on success, false on failure.
> + */
> +int
> +dt_init(struct dt_env *env);
> +
> +/**
> + * Delete dnstap environment object. Closes dnstap I/O socket and deletes all
> + * per-worker I/O queues.
> + */
> +void
> +dt_delete(struct dt_env *env);
> +
> +/**
> + * Create and send a new dnstap "Message" event of type AUTH_QUERY.
> + * @param env: dnstap environment object.
> + * @param addr: address/port of client.
> + * @param is_tcp: true for tcp, false for udp.
> + * @param zone: zone name, or NULL. in wireformat.
> + * @param zonelen: length of zone in bytes.
> + * @param pkt: query message.
> + * @param pktlen: length of pkt.
> + */
> +void
> +dt_msg_send_auth_query(struct dt_env *env,
> +#ifdef INET6
> + struct sockaddr_storage* addr,
> +#else
> + struct sockaddr_in* addr,
> +#endif
> + int is_tcp, uint8_t* zone, size_t zonelen, uint8_t* pkt, size_t pktlen);
> +
> +/**
> + * Create and send a new dnstap "Message" event of type AUTH_RESPONSE.
> + * @param env: dnstap environment object.
> + * @param addr: address/port of client.
> + * @param is_tcp: true for tcp, false for udp.
> + * @param zone: zone name, or NULL. in wireformat.
> + * @param zonelen: length of zone in bytes.
> + * @param pkt: response message.
> + * @param pktlen: length of pkt.
> + */
> +void
> +dt_msg_send_auth_response(struct dt_env *env,
> +#ifdef INET6
> + struct sockaddr_storage* addr,
> +#else
> + struct sockaddr_in* addr,
> +#endif
> + int is_tcp, uint8_t* zone, size_t zonelen, uint8_t* pkt, size_t pktlen);
> +
> +#endif /* USE_DNSTAP */
> +
> +#endif /* NSD_DNSTAP_H */
> diff --git dnstap/dnstap.m4 dnstap/dnstap.m4
> new file mode 100644
> index 00000000000..5b78b3e267c
> --- /dev/null
> +++ dnstap/dnstap.m4
> @@ -0,0 +1,56 @@
> +# dnstap.m4
> +
> +# dt_DNSTAP(default_dnstap_socket_path, [action-if-true], [action-if-false])
> +# --------------------------------------------------------------------------
> +# Check for required dnstap libraries and add dnstap configure args.
> +AC_DEFUN([dt_DNSTAP],
> +[
> + AC_ARG_ENABLE([dnstap],
> + AS_HELP_STRING([--enable-dnstap],
> + [Enable dnstap support (requires fstrm, protobuf-c)]),
> + [opt_dnstap=$enableval], [opt_dnstap=no])
> +
> + AC_ARG_WITH([dnstap-socket-path],
> + AS_HELP_STRING([--with-dnstap-socket-path=pathname],
> + [set default dnstap socket path]),
> + [opt_dnstap_socket_path=$withval], [opt_dnstap_socket_path="$1"])
> +
> + if test "x$opt_dnstap" != "xno"; then
> + AC_PATH_PROG([PROTOC_C], [protoc-c])
> + if test -z "$PROTOC_C"; then
> + AC_MSG_ERROR([The protoc-c program was not found. Please install
> protobuf-c!])
> + fi
> + AC_ARG_WITH([protobuf-c], AC_HELP_STRING([--with-protobuf-c=path],
> + [Path where protobuf-c is installed, for dnstap]), [
> + # workaround for protobuf-c includes at old dir before
> protobuf-c-1.0.0
> + if test -f $withval/include/google/protobuf-c/protobuf-c.h; then
> + CFLAGS="$CFLAGS -I$withval/include/google"
> + else
> + CFLAGS="$CFLAGS -I$withval/include"
> + fi
> + LDFLAGS="$LDFLAGS -L$withval/lib"
> + ], [
> + # workaround for protobuf-c includes at old dir before
> protobuf-c-1.0.0
> + if test -f /usr/include/google/protobuf-c/protobuf-c.h; then
> + CFLAGS="$CFLAGS -I/usr/include/google"
> + else
> + if test -f /usr/local/include/google/protobuf-c/protobuf-c.h; then
> + CFLAGS="$CFLAGS -I/usr/local/include/google"
> + LDFLAGS="$LDFLAGS -L/usr/local/lib"
> + fi
> + fi
> + ])
> + AC_ARG_WITH([libfstrm], AC_HELP_STRING([--with-libfstrm=path],
> + [Path where libfstrm is installed, for dnstap]), [
> + CFLAGS="$CFLAGS -I$withval/include"
> + LDFLAGS="$LDFLAGS -L$withval/lib"
> + ])
> + AC_SEARCH_LIBS([fstrm_iothr_init], [fstrm], [],
> + AC_MSG_ERROR([The fstrm library was not found. Please install fstrm!]))
> + AC_SEARCH_LIBS([protobuf_c_message_pack], [protobuf-c], [],
> + AC_MSG_ERROR([The protobuf-c library was not found. Please install
> protobuf-c!]))
> + $2
> + else
> + $3
> + fi
> +])
> diff --git dnstap/dnstap.proto dnstap/dnstap.proto
> new file mode 100644
> index 00000000000..88bfb4e9412
> --- /dev/null
> +++ dnstap/dnstap.proto
> @@ -0,0 +1,263 @@
> +// dnstap: flexible, structured event replication format for DNS software
> +//
> +// This file contains the protobuf schemas for the "dnstap" structured event
> +// replication format for DNS software.
> +
> +// Written in 2013-2014 by Farsight Security, Inc.
> +//
> +// To the extent possible under law, the author(s) have dedicated all
> +// copyright and related and neighboring rights to this file to the public
> +// domain worldwide. This file is distributed without any warranty.
> +//
> +// You should have received a copy of the CC0 Public Domain Dedication along
> +// with this file. If not, see:
> +//
> +// <http://creativecommons.org/publicdomain/zero/1.0/>.
> +syntax = "proto2";
> +
> +package dnstap;
> +
> +// "Dnstap": this is the top-level dnstap type, which is a "union" type that
> +// contains other kinds of dnstap payloads, although currently only one type
> +// of dnstap payload is defined.
> +// See: https://developers.google.com/protocol-buffers/docs/techniques#union
> +message Dnstap {
> + // DNS server identity.
> + // If enabled, this is the identity string of the DNS server which
> generated
> + // this message. Typically this would be the same string as returned by
> an
> + // "NSID" (RFC 5001) query.
> + optional bytes identity = 1;
> +
> + // DNS server version.
> + // If enabled, this is the version string of the DNS server which
> generated
> + // this message. Typically this would be the same string as returned by a
> + // "version.bind" query.
> + optional bytes version = 2;
> +
> + // Extra data for this payload.
> + // This field can be used for adding an arbitrary byte-string annotation
> to
> + // the payload. No encoding or interpretation is applied or enforced.
> + optional bytes extra = 3;
> +
> + // Identifies which field below is filled in.
> + enum Type {
> + MESSAGE = 1;
> + }
> + required Type type = 15;
> +
> + // One of the following will be filled in.
> + optional Message message = 14;
> +}
> +
> +// SocketFamily: the network protocol family of a socket. This specifies how
> +// to interpret "network address" fields.
> +enum SocketFamily {
> + INET = 1; // IPv4 (RFC 791)
> + INET6 = 2; // IPv6 (RFC 2460)
> +}
> +
> +// SocketProtocol: the transport protocol of a socket. This specifies how to
> +// interpret "transport port" fields.
> +enum SocketProtocol {
> + UDP = 1; // User Datagram Protocol (RFC 768)
> + TCP = 2; // Transmission Control Protocol (RFC 793)
> +}
> +
> +// Message: a wire-format (RFC 1035 section 4) DNS message and associated
> +// metadata. Applications generating "Message" payloads should follow
> +// certain requirements based on the MessageType, see below.
> +message Message {
> +
> + // There are eight types of "Message" defined that correspond to the
> + // four arrows in the following diagram, slightly modified from RFC 1035
> + // section 2:
> +
> + // +---------+ +----------+ +--------+
> + // | | query | | query | |
> + // | Stub |-SQ--------CQ->| Recursive|-RQ----AQ->| Auth. |
> + // | Resolver| | Server | | Name |
> + // | |<-SR--------CR-| |<-RR----AR-| Server |
> + // +---------+ response | | response | |
> + // +----------+ +--------+
> +
> + // Each arrow has two Type values each, one for each "end" of each arrow,
> + // because these are considered to be distinct events. Each end of each
> + // arrow on the diagram above has been marked with a two-letter Type
> + // mnemonic. Clockwise from upper left, these mnemonic values are:
> + //
> + // SQ: STUB_QUERY
> + // CQ: CLIENT_QUERY
> + // RQ: RESOLVER_QUERY
> + // AQ: AUTH_QUERY
> + // AR: AUTH_RESPONSE
> + // RR: RESOLVER_RESPONSE
> + // CR: CLIENT_RESPONSE
> + // SR: STUB_RESPONSE
> +
> + // Two additional types of "Message" have been defined for the
> + // "forwarding" case where an upstream DNS server is responsible for
> + // further recursion. These are not shown on the diagram above, but have
> + // the following mnemonic values:
> +
> + // FQ: FORWARDER_QUERY
> + // FR: FORWARDER_RESPONSE
> +
> + // The "Message" Type values are defined below.
> +
> + enum Type {
> + // AUTH_QUERY is a DNS query message received from a resolver by an
> + // authoritative name server, from the perspective of the
> authoritative
> + // name server.
> + AUTH_QUERY = 1;
> +
> + // AUTH_RESPONSE is a DNS response message sent from an authoritative
> + // name server to a resolver, from the perspective of the
> authoritative
> + // name server.
> + AUTH_RESPONSE = 2;
> +
> + // RESOLVER_QUERY is a DNS query message sent from a resolver to an
> + // authoritative name server, from the perspective of the resolver.
> + // Resolvers typically clear the RD (recursion desired) bit when
> + // sending queries.
> + RESOLVER_QUERY = 3;
> +
> + // RESOLVER_RESPONSE is a DNS response message received from an
> + // authoritative name server by a resolver, from the perspective of
> + // the resolver.
> + RESOLVER_RESPONSE = 4;
> +
> + // CLIENT_QUERY is a DNS query message sent from a client to a DNS
> + // server which is expected to perform further recursion, from the
> + // perspective of the DNS server. The client may be a stub resolver
> or
> + // forwarder or some other type of software which typically sets the
> RD
> + // (recursion desired) bit when querying the DNS server. The DNS
> server
> + // may be a simple forwarding proxy or it may be a full recursive
> + // resolver.
> + CLIENT_QUERY = 5;
> +
> + // CLIENT_RESPONSE is a DNS response message sent from a DNS server
> to
> + // a client, from the perspective of the DNS server. The DNS server
> + // typically sets the RA (recursion available) bit when responding.
> + CLIENT_RESPONSE = 6;
> +
> + // FORWARDER_QUERY is a DNS query message sent from a downstream DNS
> + // server to an upstream DNS server which is expected to perform
> + // further recursion, from the perspective of the downstream DNS
> + // server.
> + FORWARDER_QUERY = 7;
> +
> + // FORWARDER_RESPONSE is a DNS response message sent from an upstream
> + // DNS server performing recursion to a downstream DNS server, from
> the
> + // perspective of the downstream DNS server.
> + FORWARDER_RESPONSE = 8;
> +
> + // STUB_QUERY is a DNS query message sent from a stub resolver to a
> DNS
> + // server, from the perspective of the stub resolver.
> + STUB_QUERY = 9;
> +
> + // STUB_RESPONSE is a DNS response message sent from a DNS server to
> a
> + // stub resolver, from the perspective of the stub resolver.
> + STUB_RESPONSE = 10;
> + }
> +
> + // One of the Type values described above.
> + required Type type = 1;
> +
> + // One of the SocketFamily values described above.
> + optional SocketFamily socket_family = 2;
> +
> + // One of the SocketProtocol values described above.
> + optional SocketProtocol socket_protocol = 3;
> +
> + // The network address of the message initiator.
> + // For SocketFamily INET, this field is 4 octets (IPv4 address).
> + // For SocketFamily INET6, this field is 16 octets (IPv6 address).
> + optional bytes query_address = 4;
> +
> + // The network address of the message responder.
> + // For SocketFamily INET, this field is 4 octets (IPv4 address).
> + // For SocketFamily INET6, this field is 16 octets (IPv6 address).
> + optional bytes response_address = 5;
> +
> + // The transport port of the message initiator.
> + // This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
> + optional uint32 query_port = 6;
> +
> + // The transport port of the message responder.
> + // This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
> + optional uint32 response_port = 7;
> +
> + // The time at which the DNS query message was sent or received,
> depending
> + // on whether this is an AUTH_QUERY, RESOLVER_QUERY, or CLIENT_QUERY.
> + // This is the number of seconds since the UNIX epoch.
> + optional uint64 query_time_sec = 8;
> +
> + // The time at which the DNS query message was sent or received.
> + // This is the seconds fraction, expressed as a count of nanoseconds.
> + optional fixed32 query_time_nsec = 9;
> +
> + // The initiator's original wire-format DNS query message, verbatim.
> + optional bytes query_message = 10;
> +
> + // The "zone" or "bailiwick" pertaining to the DNS query message.
> + // This is a wire-format DNS domain name.
> + optional bytes query_zone = 11;
> +
> + // The time at which the DNS response message was sent or received,
> + // depending on whether this is an AUTH_RESPONSE, RESOLVER_RESPONSE, or
> + // CLIENT_RESPONSE.
> + // This is the number of seconds since the UNIX epoch.
> + optional uint64 response_time_sec = 12;
> +
> + // The time at which the DNS response message was sent or received.
> + // This is the seconds fraction, expressed as a count of nanoseconds.
> + optional fixed32 response_time_nsec = 13;
> +
> + // The responder's original wire-format DNS response message, verbatim.
> + optional bytes response_message = 14;
> +}
> +
> +// All fields except for 'type' in the Message schema are optional.
> +// It is recommended that at least the following fields be filled in for
> +// particular types of Messages.
> +
> +// AUTH_QUERY:
> +// socket_family, socket_protocol
> +// query_address, query_port
> +// query_message
> +// query_time_sec, query_time_nsec
> +
> +// AUTH_RESPONSE:
> +// socket_family, socket_protocol
> +// query_address, query_port
> +// query_time_sec, query_time_nsec
> +// response_message
> +// response_time_sec, response_time_nsec
> +
> +// RESOLVER_QUERY:
> +// socket_family, socket_protocol
> +// query_name, query_type, query_class
> +// query_message
> +// query_time_sec, query_time_nsec
> +// query_zone
> +// response_address, response_port
> +
> +// RESOLVER_RESPONSE:
> +// socket_family, socket_protocol
> +// query_name, query_type, query_class
> +// query_time_sec, query_time_nsec
> +// query_zone
> +// response_address, response_port
> +// response_message
> +// response_time_sec, response_time_nsec
> +
> +// CLIENT_QUERY:
> +// socket_family, socket_protocol
> +// query_message
> +// query_time_sec, query_time_nsec
> +
> +// CLIENT_RESPONSE:
> +// socket_family, socket_protocol
> +// query_time_sec, query_time_nsec
> +// response_message
> +// response_time_sec, response_time_nsec
> diff --git dnstap/dnstap_collector.c dnstap/dnstap_collector.c
> new file mode 100644
> index 00000000000..091113fc45f
> --- /dev/null
> +++ dnstap/dnstap_collector.c
> @@ -0,0 +1,516 @@
> +/*
> + * dnstap/dnstap_collector.c -- nsd collector process for dnstap information
> + *
> + * Copyright (c) 2018, NLnet Labs. All rights reserved.
> + *
> + * See LICENSE for the license.
> + *
> + */
> +
> +#include "config.h"
> +#include <sys/types.h>
> +#include <sys/socket.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <unistd.h>
> +#ifndef USE_MINI_EVENT
> +# ifdef HAVE_EVENT_H
> +# include <event.h>
> +# else
> +# include <event2/event.h>
> +# include "event2/event_struct.h"
> +# include "event2/event_compat.h"
> +# endif
> +#else
> +# include "mini_event.h"
> +#endif
> +#include "dnstap/dnstap_collector.h"
> +#include "dnstap/dnstap.h"
> +#include "util.h"
> +#include "nsd.h"
> +#include "region-allocator.h"
> +#include "buffer.h"
> +#include "namedb.h"
> +#include "options.h"
> +
> +struct dt_collector* dt_collector_create(struct nsd* nsd)
> +{
> + int i, sv[2];
> + struct dt_collector* dt_col = (struct dt_collector*)xalloc_zero(
> + sizeof(*dt_col));
> + dt_col->count = nsd->child_count;
> + dt_col->dt_env = NULL;
> + dt_col->region = region_create(xalloc, free);
> + dt_col->send_buffer = buffer_create(dt_col->region,
> + /* msglen + is_response + addrlen + is_tcp + packetlen + packet
> + zonelen + zone + spare + addr */
> + 4+1+4+1+4+TCP_MAX_MESSAGE_LEN+4+MAXHOSTNAMELEN + 32 +
> +#ifdef INET6
> + sizeof(struct sockaddr_storage)
> +#else
> + sizeof(struct sockaddr_in)
> +#endif
> + );
> +
> + /* open pipes in struct nsd */
> + nsd->dt_collector_fd_send = (int*)xalloc_array_zero(dt_col->count,
> + sizeof(int));
> + nsd->dt_collector_fd_recv = (int*)xalloc_array_zero(dt_col->count,
> + sizeof(int));
> + for(i=0; i<dt_col->count; i++) {
> + int fd[2];
> + fd[0] = -1;
> + fd[1] = -1;
> + if(pipe(fd) < 0) {
> + error("dnstap_collector: cannot create pipe: %s",
> + strerror(errno));
> + }
> + if(fcntl(fd[0], F_SETFL, O_NONBLOCK) == -1) {
> + log_msg(LOG_ERR, "fcntl failed: %s", strerror(errno));
> + }
> + if(fcntl(fd[1], F_SETFL, O_NONBLOCK) == -1) {
> + log_msg(LOG_ERR, "fcntl failed: %s", strerror(errno));
> + }
> + nsd->dt_collector_fd_recv[i] = fd[0];
> + nsd->dt_collector_fd_send[i] = fd[1];
> + }
> +
> + /* open socketpair */
> + if(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
> + error("dnstap_collector: cannot create socketpair: %s",
> + strerror(errno));
> + }
> + if(fcntl(sv[0], F_SETFL, O_NONBLOCK) == -1) {
> + log_msg(LOG_ERR, "fcntl failed: %s", strerror(errno));
> + }
> + if(fcntl(sv[1], F_SETFL, O_NONBLOCK) == -1) {
> + log_msg(LOG_ERR, "fcntl failed: %s", strerror(errno));
> + }
> + dt_col->cmd_socket_dt = sv[0];
> + dt_col->cmd_socket_nsd = sv[1];
> +
> + return dt_col;
> +}
> +
> +void dt_collector_destroy(struct dt_collector* dt_col, struct nsd* nsd)
> +{
> + if(!dt_col) return;
> + free(nsd->dt_collector_fd_recv);
> + nsd->dt_collector_fd_recv = NULL;
> + free(nsd->dt_collector_fd_send);
> + nsd->dt_collector_fd_send = NULL;
> + region_destroy(dt_col->region);
> + free(dt_col);
> +}
> +
> +void dt_collector_close(struct dt_collector* dt_col, struct nsd* nsd)
> +{
> + int i;
> + if(!dt_col) return;
> + if(dt_col->cmd_socket_dt != -1) {
> + close(dt_col->cmd_socket_dt);
> + dt_col->cmd_socket_dt = -1;
> + }
> + if(dt_col->cmd_socket_nsd != -1) {
> + close(dt_col->cmd_socket_nsd);
> + dt_col->cmd_socket_nsd = -1;
> + }
> + for(i=0; i<dt_col->count; i++) {
> + if(nsd->dt_collector_fd_recv[i] != -1) {
> + close(nsd->dt_collector_fd_recv[i]);
> + nsd->dt_collector_fd_recv[i] = -1;
> + }
> + if(nsd->dt_collector_fd_send[i] != -1) {
> + close(nsd->dt_collector_fd_send[i]);
> + nsd->dt_collector_fd_send[i] = -1;
> + }
> + }
> +}
> +
> +/* handle command from nsd to dt collector.
> + * mostly, check for fd closed, this means we have to exit */
> +void
> +dt_handle_cmd_from_nsd(int ATTR_UNUSED(fd), short event, void* arg)
> +{
> + struct dt_collector* dt_col = (struct dt_collector*)arg;
> + if((event&EV_READ) != 0) {
> + event_base_loopexit(dt_col->event_base, NULL);
> + }
> +}
> +
> +/* read data from fd into buffer, true when message is complete */
> +static int read_into_buffer(int fd, struct buffer* buf)
> +{
> + size_t msglen;
> + ssize_t r;
> + if(buffer_position(buf) < 4) {
> + /* read the length of the message */
> + r = read(fd, buffer_current(buf), 4 - buffer_position(buf));
> + if(r == -1) {
> + if(errno == EAGAIN || errno == EINTR) {
> + /* continue to read later */
> + return 0;
> + }
> + log_msg(LOG_ERR, "dnstap collector: read failed: %s",
> + strerror(errno));
> + return 0;
> + }
> + buffer_skip(buf, r);
> + if(buffer_position(buf) < 4)
> + return 0; /* continue to read more msglen later */
> + }
> +
> + /* msglen complete */
> + msglen = buffer_read_u32_at(buf, 0);
> + /* assert we have enough space, if we don't and we wanted to continue,
> + * we would have to skip the message somehow, but that should never
> + * happen because send_buffer and receive_buffer have the same size */
> + assert(buffer_capacity(buf) >= msglen + 4);
> + r = read(fd, buffer_current(buf), msglen - (buffer_position(buf) - 4));
> + if(r == -1) {
> + if(errno == EAGAIN || errno == EINTR) {
> + /* continue to read later */
> + return 0;
> + }
> + log_msg(LOG_ERR, "dnstap collector: read failed: %s",
> + strerror(errno));
> + return 0;
> + }
> + buffer_skip(buf, r);
> + if(buffer_position(buf) < 4 + msglen)
> + return 0; /* read more msg later */
> +
> + /* msg complete */
> + buffer_flip(buf);
> + return 1;
> +}
> +
> +/* submit the content of the buffer received to dnstap */
> +static void
> +dt_submit_content(struct dt_env* dt_env, struct buffer* buf)
> +{
> + uint8_t is_response, is_tcp;
> +#ifdef INET6
> + struct sockaddr_storage addr;
> +#else
> + struct sockaddr_in addr;
> +#endif
> + socklen_t addrlen;
> + size_t pktlen;
> + uint8_t* data;
> + size_t zonelen;
> + uint8_t* zone;
> +
> + /* parse content from buffer */
> + if(!buffer_available(buf, 4+1+4)) return;
> + buffer_skip(buf, 4); /* skip msglen */
> + is_response = buffer_read_u8(buf);
> + addrlen = buffer_read_u32(buf);
> + if(addrlen > sizeof(addr)) return;
> + if(!buffer_available(buf, addrlen)) return;
> + buffer_read(buf, &addr, addrlen);
> + if(!buffer_available(buf, 1+4)) return;
> + is_tcp = buffer_read_u8(buf);
> + pktlen = buffer_read_u32(buf);
> + if(!buffer_available(buf, pktlen)) return;
> + data = buffer_current(buf);
> + buffer_skip(buf, pktlen);
> + if(!buffer_available(buf, 4)) return;
> + zonelen = buffer_read_u32(buf);
> + if(zonelen == 0) {
> + zone = NULL;
> + } else {
> + if(zonelen > MAXDOMAINLEN) return;
> + if(!buffer_available(buf, zonelen)) return;
> + zone = buffer_current(buf);
> + buffer_skip(buf, zonelen);
> + }
> +
> + /* submit it */
> + if(is_response) {
> + dt_msg_send_auth_response(dt_env, &addr, is_tcp, zone,
> + zonelen, data, pktlen);
> + } else {
> + dt_msg_send_auth_query(dt_env, &addr, is_tcp, zone,
> + zonelen, data, pktlen);
> + }
> +}
> +
> +/* handle input from worker for dnstap */
> +void
> +dt_handle_input(int fd, short event, void* arg)
> +{
> + struct dt_collector_input* dt_input = (struct dt_collector_input*)arg;
> + if((event&EV_READ) != 0) {
> + /* read */
> + if(!read_into_buffer(fd, dt_input->buffer))
> + return;
> +
> + /* once data is complete, write it to dnstap */
> + VERBOSITY(4, (LOG_INFO, "dnstap collector: received msg len %d",
> + (int)buffer_remaining(dt_input->buffer)));
> + if(dt_input->dt_collector->dt_env) {
> + dt_submit_content(dt_input->dt_collector->dt_env,
> + dt_input->buffer);
> + }
> +
> + /* clear buffer for next message */
> + buffer_clear(dt_input->buffer);
> + }
> +}
> +
> +/* init dnstap */
> +static void dt_init_dnstap(struct dt_collector* dt_col, struct nsd* nsd)
> +{
> + int num_workers = 1;
> +#ifdef HAVE_CHROOT
> + if(nsd->chrootdir && nsd->chrootdir[0]) {
> + int l = strlen(nsd->chrootdir)-1; /* ends in trailing slash */
> + if (nsd->options->dnstap_socket_path &&
> + nsd->options->dnstap_socket_path[0] == '/' &&
> + strncmp(nsd->options->dnstap_socket_path,
> + nsd->chrootdir, l) == 0)
> + nsd->options->dnstap_socket_path += l;
> + }
> +#endif
> + dt_col->dt_env = dt_create(nsd->options->dnstap_socket_path,
> num_workers);
> + if(!dt_col->dt_env) {
> + log_msg(LOG_ERR, "could not create dnstap env");
> + return;
> + }
> + dt_apply_cfg(dt_col->dt_env, nsd->options);
> + dt_init(dt_col->dt_env);
> +}
> +
> +/* cleanup dt collector process for exit */
> +static void dt_collector_cleanup(struct dt_collector* dt_col, struct nsd*
> nsd)
> +{
> + int i;
> + dt_delete(dt_col->dt_env);
> + event_del(dt_col->cmd_event);
> + for(i=0; i<dt_col->count; i++) {
> + event_del(dt_col->inputs[i].event);
> + }
> + dt_collector_close(dt_col, nsd);
> + event_base_free(dt_col->event_base);
> +#ifdef MEMCLEAN
> + free(dt_col->cmd_event);
> + if(dt_col->inputs) {
> + for(i=0; i<dt_col->count; i++) {
> + free(dt_col->inputs[i].event);
> + }
> + free(dt_col->inputs);
> + }
> + dt_collector_destroy(dt_col, nsd);
> +#endif
> +}
> +
> +/* attach events to the event base to listen to the workers and cmd channel
> */
> +static void dt_attach_events(struct dt_collector* dt_col, struct nsd* nsd)
> +{
> + int i;
> + /* create event base */
> + dt_col->event_base = nsd_child_event_base();
> + if(!dt_col->event_base) {
> + error("dnstap collector: event_base create failed");
> + }
> +
> + /* add command handler */
> + dt_col->cmd_event = (struct event*)xalloc_zero(
> + sizeof(*dt_col->cmd_event));
> + event_set(dt_col->cmd_event, dt_col->cmd_socket_dt,
> + EV_PERSIST|EV_READ, dt_handle_cmd_from_nsd, dt_col);
> + if(event_base_set(dt_col->event_base, dt_col->cmd_event) != 0)
> + log_msg(LOG_ERR, "dnstap collector: event_base_set failed");
> + if(event_add(dt_col->cmd_event, NULL) != 0)
> + log_msg(LOG_ERR, "dnstap collector: event_add failed");
> +
> + /* add worker input handlers */
> + dt_col->inputs = xalloc_array_zero(dt_col->count,
> + sizeof(*dt_col->inputs));
> + for(i=0; i<dt_col->count; i++) {
> + dt_col->inputs[i].dt_collector = dt_col;
> + dt_col->inputs[i].event = (struct event*)xalloc_zero(
> + sizeof(struct event));
> + event_set(dt_col->inputs[i].event,
> + nsd->dt_collector_fd_recv[i], EV_PERSIST|EV_READ,
> + dt_handle_input, &dt_col->inputs[i]);
> + if(event_base_set(dt_col->event_base,
> + dt_col->inputs[i].event) != 0)
> + log_msg(LOG_ERR, "dnstap collector: event_base_set
> failed");
> + if(event_add(dt_col->inputs[i].event, NULL) != 0)
> + log_msg(LOG_ERR, "dnstap collector: event_add failed");
> +
> + dt_col->inputs[i].buffer = buffer_create(dt_col->region,
> + /* msglen + is_response + addrlen + is_tcp + packetlen
> + packet + zonelen + zone + spare + addr */
> + 4+1+4+1+4+TCP_MAX_MESSAGE_LEN+4+MAXHOSTNAMELEN + 32 +
> +#ifdef INET6
> + sizeof(struct sockaddr_storage)
> +#else
> + sizeof(struct sockaddr_in)
> +#endif
> + );
> + assert(buffer_capacity(dt_col->inputs[i].buffer) ==
> + buffer_capacity(dt_col->send_buffer));
> + }
> +}
> +
> +/* the dnstap collector process main routine */
> +static void dt_collector_run(struct dt_collector* dt_col, struct nsd* nsd)
> +{
> + /* init dnstap */
> + VERBOSITY(1, (LOG_INFO, "dnstap collector started"));
> + dt_init_dnstap(dt_col, nsd);
> + dt_attach_events(dt_col, nsd);
> +
> + /* run */
> + if(event_base_loop(dt_col->event_base, 0) == -1) {
> + error("dnstap collector: event_base_loop failed");
> + }
> +
> + /* cleanup and done */
> + VERBOSITY(1, (LOG_INFO, "dnstap collector stopped"));
> + dt_collector_cleanup(dt_col, nsd);
> + exit(0);
> +}
> +
> +void dt_collector_start(struct dt_collector* dt_col, struct nsd* nsd)
> +{
> + /* fork */
> + dt_col->dt_pid = fork();
> + if(dt_col->dt_pid == -1) {
> + error("dnstap_collector: fork failed: %s", strerror(errno));
> + }
> + if(dt_col->dt_pid == 0) {
> + /* the dt collector process is this */
> + /* close the nsd side of the command channel */
> + close(dt_col->cmd_socket_nsd);
> + dt_col->cmd_socket_nsd = -1;
> + dt_collector_run(dt_col, nsd);
> + /* NOTREACH */
> + exit(0);
> + } else {
> + /* the parent continues on, with starting NSD */
> + /* close the dt side of the command channel */
> + close(dt_col->cmd_socket_dt);
> + dt_col->cmd_socket_dt = -1;
> + }
> +}
> +
> +/* put data for sending to the collector process into the buffer */
> +static int
> +prep_send_data(struct buffer* buf, uint8_t is_response,
> +#ifdef INET6
> + struct sockaddr_storage* addr,
> +#else
> + struct sockaddr_in* addr,
> +#endif
> + socklen_t addrlen, int is_tcp, struct buffer* packet,
> + struct zone* zone)
> +{
> + buffer_clear(buf);
> + if(!buffer_available(buf, 4+1+4+addrlen+1+4+buffer_remaining(packet)))
> + return 0; /* does not fit in send_buffer, log is dropped */
> + buffer_skip(buf, 4); /* the length of the message goes here */
> + buffer_write_u8(buf, is_response);
> + buffer_write_u32(buf, addrlen);
> + buffer_write(buf, addr, (size_t)addrlen);
> + buffer_write_u8(buf, (is_tcp?1:0));
> + buffer_write_u32(buf, buffer_remaining(packet));
> + buffer_write(buf, buffer_begin(packet), buffer_remaining(packet));
> + if(zone && zone->apex && domain_dname(zone->apex)) {
> + if(!buffer_available(buf, 4 +
> domain_dname(zone->apex)->name_size))
> + return 0;
> + buffer_write_u32(buf, domain_dname(zone->apex)->name_size);
> + buffer_write(buf, dname_name(domain_dname(zone->apex)),
> + domain_dname(zone->apex)->name_size);
> + } else {
> + if(!buffer_available(buf, 4))
> + return 0;
> + buffer_write_u32(buf, 0);
> + }
> +
> + buffer_flip(buf);
> + /* write length of message */
> + buffer_write_u32_at(buf, 0, buffer_remaining(buf)-4);
> + return 1;
> +}
> +
> +/* attempt to write buffer to socket, if it blocks do not write it. */
> +static void attempt_to_write(int s, uint8_t* data, size_t len)
> +{
> + size_t total = 0;
> + ssize_t r;
> + while(total < len) {
> + r = write(s, data+total, len-total);
> + if(r == -1) {
> + if(errno == EAGAIN && total == 0) {
> + /* on first write part, check if pipe is full,
> + * if the nonblocking fd blocks, then drop
> + * the message */
> + return;
> + }
> + if(errno != EAGAIN && errno != EINTR) {
> + /* some sort of error, print it and drop it */
> + log_msg(LOG_ERR,
> + "dnstap collector: write failed: %s",
> + strerror(errno));
> + return;
> + }
> + /* continue and write this again */
> + /* for EINTR, we have to do this,
> + * for EAGAIN, if the first part succeeded, we have
> + * to continue to write the remainder of the message,
> + * because otherwise partial messages confuse the
> + * receiver. */
> + continue;
> + }
> + total += r;
> + }
> +}
> +
> +void dt_collector_submit_auth_query(struct nsd* nsd,
> +#ifdef INET6
> + struct sockaddr_storage* addr,
> +#else
> + struct sockaddr_in* addr,
> +#endif
> + socklen_t addrlen, int is_tcp, struct buffer* packet)
> +{
> + if(!nsd->dt_collector) return;
> + if(!nsd->options->dnstap_log_auth_query_messages) return;
> + VERBOSITY(4, (LOG_INFO, "dnstap submit auth query"));
> +
> + /* marshal data into send buffer */
> + if(!prep_send_data(nsd->dt_collector->send_buffer, 0, addr, addrlen,
> + is_tcp, packet, NULL))
> + return; /* probably did not fit in buffer */
> +
> + /* attempt to send data; do not block */
> + attempt_to_write(nsd->dt_collector_fd_send[nsd->this_child->child_num],
> + buffer_begin(nsd->dt_collector->send_buffer),
> + buffer_remaining(nsd->dt_collector->send_buffer));
> +}
> +
> +void dt_collector_submit_auth_response(struct nsd* nsd,
> +#ifdef INET6
> + struct sockaddr_storage* addr,
> +#else
> + struct sockaddr_in* addr,
> +#endif
> + socklen_t addrlen, int is_tcp, struct buffer* packet,
> + struct zone* zone)
> +{
> + if(!nsd->dt_collector) return;
> + if(!nsd->options->dnstap_log_auth_response_messages) return;
> + VERBOSITY(4, (LOG_INFO, "dnstap submit auth response"));
> +
> + /* marshal data into send buffer */
> + if(!prep_send_data(nsd->dt_collector->send_buffer, 1, addr, addrlen,
> + is_tcp, packet, zone))
> + return; /* probably did not fit in buffer */
> +
> + /* attempt to send data; do not block */
> + attempt_to_write(nsd->dt_collector_fd_send[nsd->this_child->child_num],
> + buffer_begin(nsd->dt_collector->send_buffer),
> + buffer_remaining(nsd->dt_collector->send_buffer));
> +}
> diff --git dnstap/dnstap_collector.h dnstap/dnstap_collector.h
> new file mode 100644
> index 00000000000..4e0825bbaea
> --- /dev/null
> +++ dnstap/dnstap_collector.h
> @@ -0,0 +1,92 @@
> +/*
> + * dnstap/dnstap_collector.h -- nsd collector process for dnstap information
> + *
> + * Copyright (c) 2018, NLnet Labs. All rights reserved.
> + *
> + * See LICENSE for the license.
> + *
> + */
> +
> +#ifndef DNSTAP_COLLECTOR_H
> +#define DNSTAP_COLLECTOR_H
> +struct dt_env;
> +struct nsd;
> +struct event_base;
> +struct event;
> +struct dt_collector_input;
> +struct zone;
> +struct buffer;
> +struct region;
> +
> +/* information for the dnstap collector process. It collects information
> + * for dnstap from the worker processes. And writes them to the dnstap
> + * socket. */
> +struct dt_collector {
> + /* dnstap env for the write to the dnstap socket */
> + struct dt_env* dt_env;
> + /* number of workers to collect from */
> + int count;
> + /* socketpair for communication between (xfrd) and the
> + * dnstap collector process. If closed, the collector process
> + * exits. The collector closes the other side of the socketpair, so
> + * that if xfrd exits, so does the dnstap collector */
> + int cmd_socket_dt, cmd_socket_nsd;
> + /* the pid of the dt collector process (0 on that process) */
> + pid_t dt_pid;
> + /* in the collector process, the event base */
> + struct event_base* event_base;
> + /* in the collector process, the cmd handle event */
> + struct event* cmd_event;
> + /* in the collector process, array size count of input per worker */
> + struct dt_collector_input* inputs;
> + /* region for buffers */
> + struct region* region;
> + /* buffer for sending data to the collector */
> + struct buffer* send_buffer;
> +};
> +
> +/* information per worker to get input from that worker. */
> +struct dt_collector_input {
> + /* the collector this is part of (for use in callbacks) */
> + struct dt_collector* dt_collector;
> + /* the event to listen to the datagrams to process for that worker*/
> + struct event* event;
> + /* buffer to store the datagrams while they are read in */
> + struct buffer* buffer;
> +};
> +
> +/* create dt_collector process structure and dt_env */
> +struct dt_collector* dt_collector_create(struct nsd* nsd);
> +/* destroy the dt_collector structure */
> +void dt_collector_destroy(struct dt_collector* dt_col, struct nsd* nsd);
> +/* close file descriptors */
> +void dt_collector_close(struct dt_collector* dt_col, struct nsd* nsd);
> +/* start the collector process */
> +void dt_collector_start(struct dt_collector* dt_col, struct nsd* nsd);
> +
> +/* submit auth query from worker. It attempts to send it to the collector,
> + * if the nonblocking fails, then it silently skips it. So it does not block
> + * on the log.
> + */
> +void dt_collector_submit_auth_query(struct nsd* nsd,
> +#ifdef INET6
> + struct sockaddr_storage* addr,
> +#else
> + struct sockaddr_in* addr,
> +#endif
> + socklen_t addrlen, int is_tcp, struct buffer* packet);
> +
> +/* submit auth response from worker. It attempts to send it to the
> collector,
> + * if the nonblocking fails, then it silently skips it. So it does not block
> + * on the log.
> + */
> +void dt_collector_submit_auth_response(struct nsd* nsd,
> +#ifdef INET6
> + struct sockaddr_storage* addr,
> +#else
> + struct sockaddr_in* addr,
> +#endif
> + socklen_t addrlen, int is_tcp, struct buffer* packet,
> + struct zone* zone);
> +
> +#endif /* DNSTAP_COLLECTOR_H */
> diff --git dnstap/dnstap_config.h.in dnstap/dnstap_config.h.in
> new file mode 100644
> index 00000000000..c9f74893a1d
> --- /dev/null
> +++ dnstap/dnstap_config.h.in
> @@ -0,0 +1,17 @@
> +#ifndef UNBOUND_DNSTAP_CONFIG_H
> +#define UNBOUND_DNSTAP_CONFIG_H
> +
> +/*
> + * Process this file (dnstap_config.h.in) with AC_CONFIG_FILES to generate
> + * dnstap_config.h.
> + *
> + * This file exists so that USE_DNSTAP can be used without including
> config.h.
> + */
> +
> +#if @ENABLE_DNSTAP@ /* ENABLE_DNSTAP */
> +# ifndef USE_DNSTAP
> +# define USE_DNSTAP 1
> +# endif
> +#endif
> +
> +#endif /* UNBOUND_DNSTAP_CONFIG_H */
> diff --git ipc.c ipc.c
> index 4da914d7ce8..46feb0a0129 100644
> --- ipc.c
> +++ ipc.c
> @@ -43,7 +43,7 @@ ipc_child_quit(struct nsd* nsd)
>
> #ifdef MEMCLEAN /* OS collects memory pages */
> #ifdef RATELIMIT
> - rrl_deinit(nsd->this_child->child_num);
> + rrl_deinit(nsd->this_child->child_num);
> #endif
> event_base_free(nsd->event_base);
> region_destroy(nsd->server_region);
> @@ -646,13 +646,13 @@ void
> xfrd_handle_ipc(int ATTR_UNUSED(fd), short event, void* arg)
> {
> xfrd_state_type* xfrd = (xfrd_state_type*)arg;
> - if ((event & EV_READ))
> + if ((event & EV_READ))
> {
> /* first attempt to read as a signal from main
> * could block further send operations */
> xfrd_handle_ipc_read(&xfrd->ipc_handler, xfrd);
> }
> - if ((event & EV_WRITE))
> + if ((event & EV_WRITE))
> {
> if(xfrd->ipc_send_blocked) { /* wait for RELOAD_DONE */
> ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ);
> @@ -681,8 +681,8 @@ xfrd_handle_ipc(int ATTR_UNUSED(fd), short event, void*
> arg)
> static void
> xfrd_handle_ipc_read(struct event* handler, xfrd_state_type* xfrd)
> {
> - sig_atomic_t cmd;
> - int len;
> + sig_atomic_t cmd;
> + int len;
>
> if(xfrd->ipc_conn->is_reading==2) {
> buffer_type* tmp = xfrd->ipc_pass;
> @@ -730,26 +730,26 @@ xfrd_handle_ipc_read(struct event* handler,
> xfrd_state_type* xfrd)
> return;
> }
>
> - if((len = read(handler->ev_fd, &cmd, sizeof(cmd))) == -1) {
> + if((len = read(handler->ev_fd, &cmd, sizeof(cmd))) == -1) {
> if(errno != EINTR && errno != EAGAIN)
> - log_msg(LOG_ERR, "xfrd_handle_ipc: read: %s",
> - strerror(errno));
> - return;
> - }
> - if(len == 0)
> - {
> + log_msg(LOG_ERR, "xfrd_handle_ipc: read: %s",
> + strerror(errno));
> + return;
> + }
> + if(len == 0)
> + {
> /* parent closed the connection. Quit */
> DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: main closed connection."));
> xfrd->shutdown = 1;
> return;
> - }
> + }
>
> - switch(cmd) {
> - case NSD_QUIT:
> - case NSD_SHUTDOWN:
> + switch(cmd) {
> + case NSD_QUIT:
> + case NSD_SHUTDOWN:
> DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: main sent shutdown cmd."));
> - xfrd->shutdown = 1;
> - break;
> + xfrd->shutdown = 1;
> + break;
> case NSD_RELOAD_DONE:
> /* reload has finished */
> DEBUG(DEBUG_IPC,1, (LOG_INFO, "xfrd: ipc recv RELOAD_DONE"));
> @@ -785,11 +785,11 @@ xfrd_handle_ipc_read(struct event* handler,
> xfrd_state_type* xfrd)
> ipc_xfrd_set_listening(xfrd, EV_PERSIST|EV_READ|EV_WRITE);
> xfrd->need_to_send_quit = 1;
> break;
> - default:
> - log_msg(LOG_ERR, "xfrd_handle_ipc: bad mode %d (%d)",
> (int)cmd,
> + default:
> + log_msg(LOG_ERR, "xfrd_handle_ipc: bad mode %d (%d)", (int)cmd,
> (int)ntohl(cmd));
> - break;
> - }
> + break;
> + }
>
> if(xfrd->ipc_conn->is_reading) {
> /* setup read of info */
> diff --git nsd-checkconf.8.in nsd-checkconf.8.in
> index d9d7bd5342d..dccf95d690d 100644
> --- nsd-checkconf.8.in
> +++ nsd-checkconf.8.in
> @@ -1,4 +1,4 @@
> -.TH "nsd\-checkconf" "8" "Sep 25, 2018" "NLnet Labs" "nsd 4.1.25"
> +.TH "nsd\-checkconf" "8" "Dec 4, 2018" "NLnet Labs" "nsd 4.1.26"
> .\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved.
> .\" See LICENSE for the license.
> .SH "NAME"
> diff --git nsd-checkconf.c nsd-checkconf.c
> index f4044c42295..20eb42a8cf1 100644
> --- nsd-checkconf.c
> +++ nsd-checkconf.c
> @@ -404,6 +404,16 @@ config_print_zone(nsd_options_type* opt, const char* k,
> int s, const char *o,
> SERV_GET_INT(rrl_ipv4_prefix_length, o);
> SERV_GET_INT(rrl_ipv6_prefix_length, o);
> SERV_GET_INT(rrl_whitelist_ratelimit, o);
> +#endif
> +#ifdef USE_DNSTAP
> + SERV_GET_BIN(dnstap_enable, o);
> + SERV_GET_STR(dnstap_socket_path, o);
> + SERV_GET_BIN(dnstap_send_identity, o);
> + SERV_GET_BIN(dnstap_send_version, o);
> + SERV_GET_STR(dnstap_identity, o);
> + SERV_GET_STR(dnstap_version, o);
> + SERV_GET_BIN(dnstap_log_auth_query_messages, o);
> + SERV_GET_BIN(dnstap_log_auth_response_messages, o);
> #endif
> SERV_GET_INT(zonefiles_write, o);
> /* remote control */
> @@ -527,6 +537,18 @@ config_test_print_server(nsd_options_type* opt)
> printf("\tzonefiles-check: %s\n", opt->zonefiles_check?"yes":"no");
> printf("\tzonefiles-write: %d\n", opt->zonefiles_write);
>
> +#ifdef USE_DNSTAP
> + printf("\ndnstap:\n");
> + printf("\tdnstap-enable: %s\n", opt->dnstap_enable?"yes":"no");
> + print_string_var("dnstap-socket-path:", opt->dnstap_socket_path);
> + printf("\tdnstap-send-identity: %s\n",
> opt->dnstap_send_identity?"yes":"no");
> + printf("\tdnstap-send-version: %s\n",
> opt->dnstap_send_version?"yes":"no");
> + print_string_var("dnstap-identity:", opt->dnstap_identity);
> + print_string_var("dnstap-version:", opt->dnstap_version);
> + printf("\tdnstap-log-auth-query-messages: %s\n",
> opt->dnstap_log_auth_query_messages?"yes":"no");
> + printf("\tdnstap-log-auth-response-messages: %s\n",
> opt->dnstap_log_auth_response_messages?"yes":"no");
> +#endif
> +
> printf("\nremote-control:\n");
> printf("\tcontrol-enable: %s\n", opt->control_enable?"yes":"no");
> for(ip = opt->control_interface; ip; ip=ip->next)
> diff --git nsd-checkzone.8.in nsd-checkzone.8.in
> index da43863aeb5..afadb4c9215 100644
> --- nsd-checkzone.8.in
> +++ nsd-checkzone.8.in
> @@ -1,4 +1,4 @@
> -.TH "nsd\-checkzone" "8" "Sep 25, 2018" "NLnet Labs" "nsd 4.1.25"
> +.TH "nsd\-checkzone" "8" "Dec 4, 2018" "NLnet Labs" "nsd 4.1.26"
> .\" Copyright (c) 2014, NLnet Labs. All rights reserved.
> .\" See LICENSE for the license.
> .SH "NAME"
> diff --git nsd-control.8.in nsd-control.8.in
> index cabaf3d72e9..40ca171c2c4 100644
> --- nsd-control.8.in
> +++ nsd-control.8.in
> @@ -1,4 +1,4 @@
> -.TH "nsd\-control" "8" "Sep 25, 2018" "NLnet Labs" "nsd 4.1.25"
> +.TH "nsd\-control" "8" "Dec 4, 2018" "NLnet Labs" "nsd 4.1.26"
> .\" Copyright (c) 2011, NLnet Labs. All rights reserved.
> .\" See LICENSE for the license.
> .SH "NAME"
> @@ -49,7 +49,8 @@ loads it.
> .B reconfig
> Reload nsd.conf and apply changes to TSIG keys and configuration patterns,
> and apply the changes to add and remove zones that are mentioned in the
> config.
> -Other changes are not applied, such as listening ip address and port and
> chroot.
> +Other changes are not applied, such as listening ip address and port and
> chroot,
> +also per-zone statistics are not applied.
> The pattern updates means that the configuration options for
> zones (request\-xfr, zonefile, notify, ...) are updated. Also new
> patterns are available for use with the addzone command.
> @@ -88,6 +89,12 @@ inside nsd.conf itself cannot be removed this way because
> the daemon
> does not write to the nsd.conf file, you need to add such zones to the
> zonelist file to be able to delete them with the delzone command.
> .TP
> +.B changezone <zone name> <pattern name>
> +Change a zone to use the pattern for options. The zone is deleted and added
> +in one operation, changing it to use the new pattern for the zone options.
> +Zones configured in nsd.conf cannot be changed like this, instead edit
> +the nsd.conf (or the included file in nsd.conf) and reconfig.
> +.TP
> .B addzones
> Add zones read from stdin of nsd\-control. Input is read per line,
> with name space patternname on a line. For bulk additions.
> diff --git nsd-control.c nsd-control.c
> index f86a5f779fc..b83fc0c2fe3 100644
> --- nsd-control.c
> +++ nsd-control.c
> @@ -91,6 +91,7 @@ usage()
> printf(" stats_noreset peek at statistics\n");
> printf(" addzone <name> <pattern> add a new zone\n");
> printf(" delzone <name> remove a zone\n");
> + printf(" changezone <name> <pattern> change zone to use pattern\n");
> printf(" addzones add zone list on stdin {name
> space pattern newline}\n");
> printf(" delzones remove zone list on stdin {name
> newline}\n");
> printf(" write [<zone>] write changed zonefiles to
> disk\n");
> diff --git nsd.8.in nsd.8.in
> index 46425aea47a..afb19cbfe66 100644
> --- nsd.8.in
> +++ nsd.8.in
> @@ -1,9 +1,9 @@
> -.TH "NSD" "8" "Sep 25, 2018" "NLnet Labs" "NSD 4.1.25"
> +.TH "NSD" "8" "Dec 4, 2018" "NLnet Labs" "NSD 4.1.26"
> .\" 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.25.
> +\- Name Server Daemon (NSD) version 4.1.26.
> .SH "SYNOPSIS"
> .B nsd
> .RB [ \-4 ]
> diff --git nsd.c nsd.c
> index b3d499b8c9a..57bae3232d7 100644
> --- nsd.c
> +++ nsd.c
> @@ -45,6 +45,9 @@
> #include "tsig.h"
> #include "remote.h"
> #include "xfrd-disk.h"
> +#ifdef USE_DNSTAP
> +#include "dnstap/dnstap_collector.h"
> +#endif
>
> /* The server handler... */
> struct nsd nsd;
> @@ -1102,6 +1105,12 @@ main(int argc, char *argv[])
> options_zonestatnames_create(nsd.options);
> server_zonestat_alloc(&nsd);
> #endif /* USE_ZONE_STATS */
> +#ifdef USE_DNSTAP
> + if(nsd.options->dnstap_enable) {
> + nsd.dt_collector = dt_collector_create(&nsd);
> + dt_collector_start(nsd.dt_collector, &nsd);
> + }
> +#endif /* USE_DNSTAP */
>
> if(nsd.server_kind == NSD_SERVER_MAIN) {
> server_prepare_xfrd(&nsd);
> diff --git nsd.conf.5.in nsd.conf.5.in
> index 9aaddeead28..348fcda8fe3 100644
> --- nsd.conf.5.in
> +++ nsd.conf.5.in
> @@ -1,4 +1,4 @@
> -.TH "nsd.conf" "5" "Sep 25, 2018" "NLnet Labs" "nsd 4.1.25"
> +.TH "nsd.conf" "5" "Dec 4, 2018" "NLnet Labs" "nsd 4.1.26"
> .\" Copyright (c) 2001\-2008, NLnet Labs. All rights reserved.
> .\" See LICENSE for the license.
> .SH "NAME"
> diff --git nsd.conf.sample.in nsd.conf.sample.in
> index f5cd7b1da9a..bd3a6e89cab 100644
> --- nsd.conf.sample.in
> +++ nsd.conf.sample.in
> @@ -177,6 +177,18 @@ server:
> # rrl-whitelist-ratelimit: 2000
> # RRLend
>
> +# DNSTAP config section, if compiled with that
> +# dnstap:
> + # set this to yes and set one or more of dnstap-log-..-messages to yes.
> + # dnstap-enable: no
> + # dnstap-socket-path: "/var/run/dnstap.sock"
> + # dnstap-send-identity: no
> + # dnstap-send-version: no
> + # dnstap-identity: ""
> + # dnstap-version: ""
> + # dnstap-log-auth-query-messages: no
> + # dnstap-log-auth-response-messages: no
> +
> # Remote control config section.
> remote-control:
> # Enable remote control with nsd-control(8) here.
> diff --git nsd.h nsd.h
> index 903dc814282..c900ca6cbaa 100644
> --- nsd.h
> +++ nsd.h
> @@ -18,6 +18,9 @@ struct netio_handler;
> struct nsd_options;
> struct udb_base;
> struct daemon_remote;
> +#ifdef USE_DNSTAP
> +struct dt_collector;
> +#endif
>
> /* The NSD runtime states and NSD ipc command values */
> #define NSD_RUN 0
> @@ -260,6 +263,13 @@ struct nsd
> /* current zonestat array to use */
> struct nsdst* zonestatnow;
> #endif /* BIND8_STATS */
> +#ifdef USE_DNSTAP
> + /* the dnstap collector process info */
> + struct dt_collector* dt_collector;
> + /* the pipes from server processes to the dt_collector,
> + * arrays of size child_count. Kept open for (re-)forks. */
> + int *dt_collector_fd_send, *dt_collector_fd_recv;
> +#endif /* USE_DNSTAP */
> /* ratelimit for errors, time value */
> time_t err_limit_time;
> /* ratelimit for errors, packet count */
> diff --git options.c options.c
> index d9028c7305d..d28a2c45dbb 100644
> --- options.c
> +++ options.c
> @@ -97,6 +97,16 @@ nsd_options_create(region_type* region)
> opt->rrl_ratelimit = RRL_LIMIT/2;
> opt->rrl_whitelist_ratelimit = RRL_WLIST_LIMIT/2;
> # endif
> +#endif
> +#ifdef USE_DNSTAP
> + opt->dnstap_enable = 0;
> + opt->dnstap_socket_path = DNSTAP_SOCKET_PATH;
> + opt->dnstap_send_identity = 0;
> + opt->dnstap_send_version = 0;
> + opt->dnstap_identity = NULL;
> + opt->dnstap_version = NULL;
> + opt->dnstap_log_auth_query_messages = 0;
> + opt->dnstap_log_auth_response_messages = 0;
> #endif
> opt->zonefiles_check = 1;
> if(opt->database == NULL || opt->database[0] == 0)
> diff --git options.h options.h
> index 3b1ad62b9db..a83eb383e47 100644
> --- options.h
> +++ options.h
> @@ -125,6 +125,22 @@ struct nsd_options {
> /** max qps for whitelisted queries, 0 is nolimit */
> size_t rrl_whitelist_ratelimit;
> #endif
> + /** if dnstap is enabled */
> + int dnstap_enable;
> + /** dnstap socket path */
> + char* dnstap_socket_path;
> + /** true to send "identity" via dnstap */
> + int dnstap_send_identity;
> + /** true to send "version" via dnstap */
> + int dnstap_send_version;
> + /** dnstap "identity", hostname is used if "". */
> + char* dnstap_identity;
> + /** dnstap "version", package version is used if "". */
> + char* dnstap_version;
> + /** true to log dnstap AUTH_QUERY message events */
> + int dnstap_log_auth_query_messages;
> + /** true to log dnstap AUTH_RESPONSE message events */
> + int dnstap_log_auth_response_messages;
>
> region_type* region;
> };
> diff --git remote.c remote.c
> index e218ba4cc64..5cc36489d37 100644
> --- remote.c
> +++ remote.c
> @@ -1250,6 +1250,91 @@ zonestat_inc_ifneeded(xfrd_state_type* xfrd)
> #endif /* USE_ZONE_STATS */
> }
>
> +/** perform the changezone command for one zone */
> +static int
> +perform_changezone(RES* ssl, xfrd_state_type* xfrd, char* arg)
> +{
> + const dname_type* dname;
> + struct zone_options* zopt;
> + char* arg2 = NULL;
> + if(!find_arg2(ssl, arg, &arg2))
> + return 0;
> +
> + /* if we add it to the xfrd now, then xfrd could download AXFR and
> + * store it and the NSD-reload would see it in the difffile before
> + * it sees the add-config task.
> + */
> + /* thus: AXFRs and IXFRs must store the pattern name in the
> + * difffile, so that it can be added when the AXFR or IXFR is seen.
> + */
> +
> + /* check that the pattern exists */
> + if(!rbtree_search(xfrd->nsd->options->patterns, arg2)) {
> + (void)ssl_printf(ssl, "error pattern %s does not exist\n",
> + arg2);
> + return 0;
> + }
> +
> + dname = dname_parse(xfrd->region, arg);
> + if(!dname) {
> + (void)ssl_printf(ssl, "error cannot parse zone name\n");
> + return 0;
> + }
> +
> + /* see if zone is a duplicate */
> + if( (zopt=zone_options_find(xfrd->nsd->options, dname)) ) {
> + if(zopt->part_of_config) {
> + (void)ssl_printf(ssl, "error zone defined in nsd.conf, "
> + "cannot delete it in this manner: remove it from "
> + "nsd.conf yourself and repattern\n");
> + region_recycle(xfrd->region, (void*)dname,
> dname_total_size(dname));
> + dname = NULL;
> + return 0;
> + }
> + /* found the zone, now delete it */
> + /* create deletion task */
> + /* this deletion task is processed before the addition task,
> + * that is created below, in the same reload process, causing
> + * a seamless change from one to the other, with no downtime
> + * for the zone. */
> + task_new_del_zone(xfrd->nsd->task[xfrd->nsd->mytask],
> + xfrd->last_task, dname);
> + xfrd_set_reload_now(xfrd);
> + /* delete it in xfrd */
> + if(zone_is_slave(zopt)) {
> + xfrd_del_slave_zone(xfrd, dname);
> + }
> + xfrd_del_notify(xfrd, dname);
> + /* delete from config */
> + zone_list_del(xfrd->nsd->options, zopt);
> + } else {
> + (void)ssl_printf(ssl, "zone %s did not exist, creating", arg);
> + }
> + region_recycle(xfrd->region, (void*)dname, dname_total_size(dname));
> + dname = NULL;
> +
> + /* add to zonelist and adds to config in memory */
> + zopt = zone_list_add(xfrd->nsd->options, arg, arg2);
> + if(!zopt) {
> + /* also dname parse error here */
> + (void)ssl_printf(ssl, "error could not add zonelist entry\n");
> + return 0;
> + }
> + /* make addzone task and schedule reload */
> + task_new_add_zone(xfrd->nsd->task[xfrd->nsd->mytask],
> + xfrd->last_task, arg, arg2,
> + getzonestatid(xfrd->nsd->options, zopt));
> + zonestat_inc_ifneeded(xfrd);
> + xfrd_set_reload_now(xfrd);
> + /* add to xfrd - notify (for master and slaves) */
> + init_notify_send(xfrd->notify_zones, xfrd->region, zopt);
> + /* add to xfrd - slave */
> + if(zone_is_slave(zopt)) {
> + xfrd_init_slave_zone(xfrd, zopt);
> + }
> + return 1;
> +}
> +
> /** perform the addzone command for one zone */
> static int
> perform_addzone(RES* ssl, xfrd_state_type* xfrd, char* arg)
> @@ -1334,7 +1419,7 @@ perform_delzone(RES* ssl, xfrd_state_type* xfrd, char*
> arg)
> /* nothing to do */
> if(!ssl_printf(ssl, "warning zone %s not present\n", arg))
> return 0;
> - return 1;
> + return 0;
> }
>
> /* see if it can be deleted */
> @@ -1381,6 +1466,15 @@ do_delzone(RES* ssl, xfrd_state_type* xfrd, char* arg)
> send_ok(ssl);
> }
>
> +/** do the changezone command */
> +static void
> +do_changezone(RES* ssl, xfrd_state_type* xfrd, char* arg)
> +{
> + if(!perform_changezone(ssl, xfrd, arg))
> + return;
> + send_ok(ssl);
> +}
> +
> /** do the addzones command */
> static void
> do_addzones(RES* ssl, xfrd_state_type* xfrd)
> @@ -1867,6 +1961,8 @@ execute_cmd(struct daemon_remote* rc, RES* ssl, char*
> cmd, struct rc_state* rs)
> do_addzone(ssl, rc->xfrd, skipwhite(p+7));
> } else if(cmdcmp(p, "delzone", 7)) {
> do_delzone(ssl, rc->xfrd, skipwhite(p+7));
> + } else if(cmdcmp(p, "changezone", 10)) {
> + do_changezone(ssl, rc->xfrd, skipwhite(p+10));
> } else if(cmdcmp(p, "addzones", 8)) {
> do_addzones(ssl, rc->xfrd);
> } else if(cmdcmp(p, "delzones", 8)) {
> diff --git server.c server.c
> index af2f60f243a..edde352117b 100644
> --- server.c
> +++ server.c
> @@ -65,6 +65,9 @@
> #include "remote.h"
> #include "lookup3.h"
> #include "rrl.h"
> +#ifdef USE_DNSTAP
> +#include "dnstap/dnstap_collector.h"
> +#endif
>
> #define RELOAD_SYNC_TIMEOUT 25 /* seconds */
>
> @@ -599,6 +602,26 @@ server_init_ifs(struct nsd *nsd, size_t from, size_t to,
> int* reuseport_works)
> }
>
> #ifdef SO_REUSEPORT
> +# ifdef SO_REUSEPORT_LB
> + /* on FreeBSD 12 we have SO_REUSEPORT_LB that does loadbalance
> + * like SO_REUSEPORT on Linux. This is what the users want
> + * with the config option in nsd.conf; if we actually
> + * need local address and port reuse they'll also need to
> + * have SO_REUSEPORT set for them, assume it was _LB they want.
> + */
> + if(nsd->reuseport && *reuseport_works &&
> + setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_REUSEPORT_LB,
> + (void*)&on, (socklen_t)sizeof(on)) < 0) {
> + if(verbosity >= 3
> +#ifdef ENOPROTOOPT
> + || errno != ENOPROTOOPT
> +#endif
> + )
> + log_msg(LOG_ERR, "setsockopt(..., SO_REUSEPORT_LB, "
> + "...) failed: %s", strerror(errno));
> + *reuseport_works = 0;
> + }
> +# else /* SO_REUSEPORT_LB */
> if(nsd->reuseport && *reuseport_works &&
> setsockopt(nsd->udp[i].s, SOL_SOCKET, SO_REUSEPORT,
> (void*)&on, (socklen_t)sizeof(on)) < 0) {
> @@ -611,6 +634,7 @@ server_init_ifs(struct nsd *nsd, size_t from, size_t to,
> int* reuseport_works)
> "...) failed: %s", strerror(errno));
> *reuseport_works = 0;
> }
> +# endif /* SO_REUSEPORT_LB */
> #else
> (void)reuseport_works;
> #endif /* SO_REUSEPORT */
> @@ -1088,6 +1112,9 @@ server_shutdown(struct nsd *nsd)
> #ifdef MEMCLEAN /* OS collects memory pages */
> #ifdef RATELIMIT
> rrl_mmap_deinit_keep_mmap();
> +#endif
> +#ifdef USE_DNSTAP
> + dt_collector_destroy(nsd->dt_collector, nsd);
> #endif
> udb_base_free_keep_mmap(nsd->task[0]);
> udb_base_free_keep_mmap(nsd->task[1]);
> @@ -1903,6 +1930,9 @@ server_main(struct nsd *nsd)
> unlink(nsd->zonestatfname[0]);
> unlink(nsd->zonestatfname[1]);
> #endif
> +#ifdef USE_DNSTAP
> + dt_collector_close(nsd->dt_collector, nsd);
> +#endif
>
> if(reload_listener.fd != -1) {
> sig_atomic_t cmd = NSD_QUIT;
> @@ -2215,6 +2245,7 @@ handle_udp(int fd, short event, void* arg)
> for (i = 0; i < recvcount; i++) {
> loopstart:
> received = msgs[i].msg_len;
> + queries[i]->addrlen = msgs[i].msg_hdr.msg_namelen;
> q = queries[i];
> if (received == -1) {
> log_msg(LOG_ERR, "recvmmsg %d failed %s", i, strerror(
> @@ -2223,6 +2254,7 @@ handle_udp(int fd, short event, void* arg)
> /* No zone statup */
> query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0);
> iovecs[i].iov_len = buffer_remaining(q->packet);
> + msgs[i].msg_hdr.msg_namelen = queries[i]->addrlen;
> goto swap_drop;
> }
>
> @@ -2237,6 +2269,10 @@ handle_udp(int fd, short event, void* arg)
>
> buffer_skip(q->packet, received);
> buffer_flip(q->packet);
> +#ifdef USE_DNSTAP
> + dt_collector_submit_auth_query(data->nsd, &q->addr, q->addrlen,
> + q->tcp, q->packet);
> +#endif /* USE_DNSTAP */
>
> /* Process and answer the query... */
> if (server_process_query_udp(data->nsd, q) != QUERY_DISCARDED) {
> @@ -2267,9 +2303,15 @@ handle_udp(int fd, short event, void* arg)
> ZTATUP(data->nsd, q->zone, truncated);
> }
> #endif /* BIND8_STATS */
> +#ifdef USE_DNSTAP
> + dt_collector_submit_auth_response(data->nsd,
> + &q->addr, q->addrlen, q->tcp, q->packet,
> + q->zone);
> +#endif /* USE_DNSTAP */
> } else {
> query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0);
> iovecs[i].iov_len = buffer_remaining(q->packet);
> + msgs[i].msg_hdr.msg_namelen = queries[i]->addrlen;
> swap_drop:
> STATUP(data->nsd, dropped);
> ZTATUP(data->nsd, q->zone, dropped);
> @@ -2310,6 +2352,7 @@ handle_udp(int fd, short event, void* arg)
> for(i=0; i<recvcount; i++) {
> query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0);
> iovecs[i].iov_len = buffer_remaining(queries[i]->packet);
> + msgs[i].msg_hdr.msg_namelen = queries[i]->addrlen;
> }
> }
>
> @@ -2350,13 +2393,15 @@ handle_udp(int fd, short event, void* arg)
> }
> for (i = 0; i < recvcount; i++) {
> received = msgs[i].msg_len;
> - msgs[i].msg_hdr.msg_namelen = queries[i]->addrlen;
> + queries[i]->addrlen = msgs[i].msg_hdr.msg_namelen;
> if (received == -1) {
> log_msg(LOG_ERR, "recvmmsg failed");
> STATUP(data->nsd, rxerr);
> /* No zone statup */
> /* the error can be found in msgs[i].msg_hdr.msg_flags
> */
> query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0);
> + iovecs[i].iov_len =
> buffer_remaining(queries[i]->packet);
> + msgs[i].msg_hdr.msg_namelen = queries[i]->addrlen;
> continue;
> }
> q = queries[i];
> @@ -2394,6 +2439,10 @@ handle_udp(int fd, short event, void* arg)
>
> buffer_skip(q->packet, received);
> buffer_flip(q->packet);
> +#ifdef USE_DNSTAP
> + dt_collector_submit_auth_query(data->nsd, &q->addr, q->addrlen,
> + q->tcp, q->packet);
> +#endif /* USE_DNSTAP */
>
> /* Process and answer the query... */
> if (server_process_query_udp(data->nsd, q) != QUERY_DISCARDED) {
> @@ -2440,6 +2489,11 @@ handle_udp(int fd, short event, void* arg)
> ZTATUP(data->nsd, q->zone, truncated);
> }
> #endif /* BIND8_STATS */
> +#ifdef USE_DNSTAP
> + dt_collector_submit_auth_response(data->nsd,
> + &q->addr, q->addrlen, q->tcp,
> + q->packet, q->zone);
> +#endif /* USE_DNSTAP */
> }
> } else {
> STATUP(data->nsd, dropped);
> @@ -2448,6 +2502,8 @@ handle_udp(int fd, short event, void* arg)
> #ifndef NONBLOCKING_IS_BROKEN
> #ifdef HAVE_RECVMMSG
> query_reset(queries[i], UDP_MAX_MESSAGE_LEN, 0);
> + iovecs[i].iov_len = buffer_remaining(queries[i]->packet);
> + msgs[i].msg_hdr.msg_namelen = queries[i]->addrlen;
> #endif
> }
> #endif
> @@ -2633,6 +2689,10 @@ handle_tcp_reading(int fd, short event, void* arg)
> 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... */
> @@ -2668,6 +2728,11 @@ 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 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;
>
> timeout.tv_sec = data->tcp_timeout / 1000;
> diff --git xfrd.c xfrd.c
> index a23001fbc5f..c2eeff8a1e8 100644
> --- xfrd.c
> +++ xfrd.c
> @@ -30,6 +30,9 @@
> #include "ipc.h"
> #include "remote.h"
> #include "rrl.h"
> +#ifdef USE_DNSTAP
> +#include "dnstap/dnstap_collector.h"
> +#endif
>
> #ifdef HAVE_SYSTEMD
> #include <systemd/sd-daemon.h>
> @@ -398,6 +401,9 @@ xfrd_shutdown()
> #ifdef HAVE_SSL
> daemon_remote_delete(xfrd->nsd->rc); /* ssl-delete secret keys */
> #endif
> +#ifdef USE_DNSTAP
> + dt_collector_close(nsd.dt_collector, &nsd);
> +#endif
>
> /* process-exit cleans up memory used by xfrd process */
> DEBUG(DEBUG_XFRD,1, (LOG_INFO, "xfrd shutdown complete"));
> @@ -429,6 +435,9 @@ xfrd_shutdown()
> }
> #ifdef RATELIMIT
> rrl_mmap_deinit();
> +#endif
> +#ifdef USE_DNSTAP
> + dt_collector_destroy(nsd.dt_collector, &nsd);
> #endif
> udb_base_free(nsd.task[0]);
> udb_base_free(nsd.task[1]);
> diff --git zparser.y zparser.y
> index c301c9eda78..d19387d91a5 100644
> --- zparser.y
> +++ zparser.y
> @@ -298,25 +298,57 @@ rel_dname: label
>
> wire_dname: wire_abs_dname
> | wire_rel_dname
> + {
> + /* terminate in root label and copy the origin in there */
> + if(parser->origin && domain_dname(parser->origin)) {
> + $$.len = $1.len + domain_dname(parser->origin)->name_size;
> + if ($$.len > MAXDOMAINLEN)
> + zc_error("domain name exceeds %d character limit",
> + MAXDOMAINLEN);
> + $$.str = (char *) region_alloc(parser->rr_region, $$.len);
> + memmove($$.str, $1.str, $1.len);
> + memmove($$.str + $1.len,
> dname_name(domain_dname(parser->origin)),
> + domain_dname(parser->origin)->name_size);
> + } else {
> + $$.len = $1.len + 1;
> + if ($$.len > MAXDOMAINLEN)
> + zc_error("domain name exceeds %d character limit",
> + MAXDOMAINLEN);
> + $$.str = (char *) region_alloc(parser->rr_region, $$.len);
> + memmove($$.str, $1.str, $1.len);
> + $$.str[ $1.len ] = 0;
> + }
> + }
> ;
>
> wire_abs_dname: '.'
> {
> - char *result = (char *) region_alloc(parser->rr_region, 2);
> + char *result = (char *) region_alloc(parser->rr_region, 1);
> result[0] = 0;
> - result[1] = '\0';
> $$.str = result;
> $$.len = 1;
> }
> + | '@'
> + {
> + if(parser->origin && domain_dname(parser->origin)) {
> + $$.len = domain_dname(parser->origin)->name_size;
> + $$.str = (char *) region_alloc(parser->rr_region, $$.len);
> + memmove($$.str, dname_name(domain_dname(parser->origin)),
> $$.len);
> + } else {
> + $$.len = 1;
> + $$.str = (char *) region_alloc(parser->rr_region, $$.len);
> + $$.str[0] = 0;
> + }
> + }
> | wire_rel_dname '.'
> {
> - char *result = (char *) region_alloc(parser->rr_region,
> - $1.len + 2);
> - memcpy(result, $1.str, $1.len);
> - result[$1.len] = 0;
> - result[$1.len+1] = '\0';
> - $$.str = result;
> $$.len = $1.len + 1;
> + if ($$.len > MAXDOMAINLEN)
> + zc_error("domain name exceeds %d character limit",
> + MAXDOMAINLEN);
> + $$.str = (char *) region_alloc(parser->rr_region, $$.len);
> + memcpy($$.str, $1.str, $1.len);
> + $$.str[$1.len] = 0;
> }
> ;
>
> @@ -330,7 +362,7 @@ wire_label: STR
>
> /* make label anyway */
> result[0] = $1.len;
> - memcpy(result+1, $1.str, $1.len);
> + memmove(result+1, $1.str, $1.len);
>
> $$.str = result;
> $$.len = $1.len + 1;
> @@ -340,16 +372,13 @@ wire_label: STR
> wire_rel_dname: wire_label
> | wire_rel_dname '.' wire_label
> {
> - if ($1.len + $3.len - 3 > MAXDOMAINLEN)
> + $$.len = $1.len + $3.len;
> + if ($$.len > MAXDOMAINLEN)
> zc_error("domain name exceeds %d character limit",
> MAXDOMAINLEN);
> -
> - /* make dname anyway */
> - $$.len = $1.len + $3.len;
> - $$.str = (char *) region_alloc(parser->rr_region, $$.len + 1);
> - memcpy($$.str, $1.str, $1.len);
> - memcpy($$.str + $1.len, $3.str, $3.len);
> - $$.str[$$.len] = '\0';
> + $$.str = (char *) region_alloc(parser->rr_region, $$.len);
> + memmove($$.str, $1.str, $1.len);
> + memmove($$.str + $1.len, $3.str, $3.len);
> }
> ;
>
>
>
> --
> I'm not entirely sure you are real.
>
--
I'm not entirely sure you are real.