*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 <nsd-b...@nlnetlabs.nl>.
>  #
> @@ -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='nsd-b...@nlnetlabs.nl'
>  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,nsd-b...@nlnetlabs.nl)
> +AC_INIT(NSD,4.1.26,nsd-b...@nlnetlabs.nl)
>  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.

Reply via email to