*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.