From: Evan Hunt Date: Wed, 13 Jul 2016 08:12:47 +0000 (-0700) Subject: [master] rndc dnstap -roll X-Git-Tag: v9.12.0a0~186 X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=ffa622d7a398cb1bb2f4e0db296c7a6829a8c90b;p=thirdparty%2Fbind9.git [master] rndc dnstap -roll 4411. [func] "rndc dnstap -roll" automatically rolls the dnstap output file; the previous version is saved with ".0" suffix, and earlier versions with ".1" and so on. An optional numeric argument indicates how many prior files to save. [RT #42830] --- diff --git a/CHANGES b/CHANGES index f2f57d7e4e7..8a50133630b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,9 @@ +4411. [func] "rndc dnstap -roll" automatically rolls the + dnstap output file; the previous version is + saved with ".0" suffix, and earlier versions + with ".1" and so on. An optional numeric argument + indicates how many prior files to save. [RT #42830] + 4410. [bug] Address use after free and memory leak with dnstap. [RT #42746] diff --git a/bin/named/control.c b/bin/named/control.c index be3286e3833..bd2f36b6802 100644 --- a/bin/named/control.c +++ b/bin/named/control.c @@ -272,8 +272,9 @@ ns_control_docommand(isccc_sexpr_t *message, isc_boolean_t readonly, result = ns_server_testgen(lex, text); } else if (command_compare(command, NS_COMMAND_MKEYS)) { result = ns_server_mkeys(ns_g_server, lex, text); - } else if (command_compare(command, NS_COMMAND_DNSTAPREOPEN)) { - result = ns_server_dnstap_reopen(ns_g_server); + } else if (command_compare(command, NS_COMMAND_DNSTAP) || + command_compare(command, NS_COMMAND_DNSTAPREOPEN)) { + result = ns_server_dnstap(ns_g_server, lex, text); } else { isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_CONTROL, ISC_LOG_WARNING, diff --git a/bin/named/include/named/control.h b/bin/named/include/named/control.h index 9fda884d66c..9925fa39f57 100644 --- a/bin/named/include/named/control.h +++ b/bin/named/include/named/control.h @@ -64,6 +64,7 @@ #define NS_COMMAND_TESTGEN "testgen" #define NS_COMMAND_MKEYS "managed-keys" #define NS_COMMAND_DNSTAPREOPEN "dnstap-reopen" +#define NS_COMMAND_DNSTAP "dnstap" isc_result_t ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp); diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index 3450e635339..da32df70c4a 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -740,6 +740,6 @@ ns_server_mkeys(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text); * Close and reopen DNSTAP output file. */ isc_result_t -ns_server_dnstap_reopen(ns_server_t *server); +ns_server_dnstap(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text); #endif /* NAMED_SERVER_H */ diff --git a/bin/named/server.c b/bin/named/server.c index 7eddd27e076..5e74f72b242 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -12211,14 +12211,45 @@ ns_server_mkeys(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text) { } isc_result_t -ns_server_dnstap_reopen(ns_server_t *server) { - +ns_server_dnstap(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text) { #if HAVE_DNSTAP - if (server->dtenv != NULL) - return(dns_dt_reopen(server->dtenv)); - return (ISC_R_NOTFOUND); + char *ptr; + + if (server->dtenv == NULL) + return (ISC_R_NOTFOUND); + + /* Check the command name. */ + ptr = next_token(lex, text); + if (ptr == NULL) + return (ISC_R_UNEXPECTEDEND); + + /* "dnstap-reopen" was used in 9.11.0b1 */ + if (strcasecmp(ptr, "dnstap-reopen") == 0) + return (dns_dt_reopen(server->dtenv, ISC_FALSE)); + + /* Find out what we are to do. */ + ptr = next_token(lex, text); + if (ptr == NULL) + return (ISC_R_UNEXPECTEDEND); + + if (strcasecmp(ptr, "-reopen") == 0) + return (dns_dt_reopen(server->dtenv, -1)); + else if ((strcasecmp(ptr, "-roll") == 0)) { + int backups = 0; + unsigned int n; + ptr = next_token(lex, text); + if (ptr != NULL) { + n = sscanf(ptr, "%u", &backups); + if (n != 1U) + return (ISC_R_BADNUMBER); + } + return (dns_dt_reopen(server->dtenv, backups)); + } else + return (DNS_R_SYNTAX); #else UNUSED(server); + UNUSED(lex); + UNUSED(text); return (ISC_R_NOTIMPLEMENTED); #endif } diff --git a/bin/rndc/rndc.docbook b/bin/rndc/rndc.docbook index 22e92203e2e..9247e4835b3 100644 --- a/bin/rndc/rndc.docbook +++ b/bin/rndc/rndc.docbook @@ -306,11 +306,18 @@ - dnstap-reopen + dnstap ( -reopen | -roll number ) - Close and re-open DNSTAP output files. This allows the files - to be renamed externally then to be re-opened. + Close and re-open DNSTAP output files. + rndc dnstap -reopen allows the output + file to be renamed externally, then re-opened. + rndc dnstap -roll causes the output file + to be rolled automatically, similar to log files; the most + recent output file has ".0" appended to its name; the + previous most recent output file is moved to ".1", and so on. + If number is specified, then the + number of backup log files is limited to that number. @@ -605,17 +612,17 @@ - recursing - - - Dump the list of queries named is currently - recursing on, and the list of domains to which iterative - queries are currently being sent. (The second list includes - the number of fetches currently active for the given domain, - and how many have been passed or dropped because of the - option.) - - + recursing + + + Dump the list of queries named is currently + recursing on, and the list of domains to which iterative + queries are currently being sent. (The second list includes + the number of fetches currently active for the given domain, + and how many have been passed or dropped because of the + option.) + + diff --git a/bin/tests/system/dnstap/tests.sh b/bin/tests/system/dnstap/tests.sh index 2351b17378c..35d9d66b3a6 100644 --- a/bin/tests/system/dnstap/tests.sh +++ b/bin/tests/system/dnstap/tests.sh @@ -15,15 +15,16 @@ status=0 $DIG +short @10.53.0.3 -p 5300 a.example > dig.out +# check three different dnstap reopen/roll methods: +# ns1: dnstap-reopen; ns2: dnstap -reopen; ns3: dnstap -roll mv ns1/dnstap.out ns1/dnstap.out.save mv ns2/dnstap.out ns2/dnstap.out.save -mv ns3/dnstap.out ns3/dnstap.out.save sleep 2 $RNDCCMD -s 10.53.0.1 dnstap-reopen | sed 's/^/I:ns1 /' -$RNDCCMD -s 10.53.0.2 dnstap-reopen | sed 's/^/I:ns2 /' -$RNDCCMD -s 10.53.0.3 dnstap-reopen | sed 's/^/I:ns3 /' +$RNDCCMD -s 10.53.0.2 dnstap -reopen | sed 's/^/I:ns2 /' +$RNDCCMD -s 10.53.0.3 dnstap -roll | sed 's/^/I:ns3 /' $DIG +short @10.53.0.3 -p 5300 a.example > dig.out sleep 1 @@ -56,6 +57,7 @@ cr2=`$DNSTAPREAD ns2/dnstap.out.save | grep "CR " | wc -l` rq2=`$DNSTAPREAD ns2/dnstap.out.save | grep "RQ " | wc -l` rr2=`$DNSTAPREAD ns2/dnstap.out.save | grep "RR " | wc -l` +mv ns3/dnstap.out.0 ns3/dnstap.out.save udp3=`$DNSTAPREAD ns3/dnstap.out.save | grep "UDP " | wc -l` tcp3=`$DNSTAPREAD ns3/dnstap.out.save | grep "TCP " | wc -l` aq3=`$DNSTAPREAD ns3/dnstap.out.save | grep "AQ " | wc -l` diff --git a/doc/arm/notes.xml b/doc/arm/notes.xml index 71b362eac8f..e13d4a37b46 100644 --- a/doc/arm/notes.xml +++ b/doc/arm/notes.xml @@ -196,8 +196,19 @@ a human-readable format. - rndc dnstap-reopen can be used reopen - dnstap output files after renaming them. + rndc dnstap -roll causes dnstap + output files to be rolled like log files -- the most recent output + file is renamed with a .0 suffix, the next + most recent with .1, etc. (Note that this + only works when dnstap output is being written + to a file, not to a UNIX domain socket.) An optional numerical + argument specifies how many backup log files to retain; if not + specified or set to 0, there is no limit. + + + rndc dnstap -reopen simply closes and reopens + the dnstap output channel without renaming + the output file. For more information on dnstap, see diff --git a/lib/dns/dnstap.c b/lib/dns/dnstap.c index b80fb67cdf9..06c16a92eb8 100644 --- a/lib/dns/dnstap.c +++ b/lib/dns/dnstap.c @@ -96,6 +96,7 @@ struct dns_dtenv { isc_region_t identity; isc_region_t version; char *path; + dns_dtmode_t mode; }; #define CHECK(x) do { \ @@ -232,6 +233,7 @@ dns_dt_create(isc_mem_t *mctx, dns_dtmode_t mode, const char *path, fstrm_writer_destroy(&fw); CHECK(ISC_R_FAILURE); } + env->mode = mode; isc_mem_attach(mctx, &env->mctx); @@ -265,25 +267,46 @@ dns_dt_create(isc_mem_t *mctx, dns_dtmode_t mode, const char *path, } isc_result_t -dns_dt_reopen(dns_dtenv_t *env) { - isc_result_t result = ISC_R_SUCCESS; +dns_dt_reopen(dns_dtenv_t *env, int roll) { + isc_result_t result; + isc_logfile_t file; fstrm_res res; REQUIRE(VALID_DTENV(env)); + if (env->fw == NULL) + return (ISC_R_FAILURE); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSTAP, DNS_LOGMODULE_DNSTAP, ISC_LOG_INFO, - "reopening dnstap destination '%s'", + "%s dnstap destination '%s'", + (roll < 0) ? "reopening" : "rolling", env->path); - if (env->fw != NULL) { - res = fstrm_writer_close(env->fw); - if (res == fstrm_res_success) - fstrm_writer_open(env->fw); - return (ISC_R_SUCCESS); + res = fstrm_writer_close(env->fw); + if (res != fstrm_res_success) + return (ISC_R_FAILURE); + + if (roll >= 0) { + /* + * Create a temporary isc_logfile_t structure so we can + * take advantage of the logfile rolling facility. + */ + char *filename = isc_mem_strdup(env->mctx, env->path); + file.name = filename; + file.stream = NULL; + file.versions = roll != 0 ? roll : ISC_LOG_ROLLINFINITE; + file.maximum_size = 0; + file.maximum_reached = ISC_FALSE; + result = isc_logfile_roll(&file); + isc_mem_free(env->mctx, filename); + if (result != ISC_R_SUCCESS) + return (result); } - return (result); + fstrm_writer_open(env->fw); + + return (ISC_R_SUCCESS); } static isc_result_t diff --git a/lib/dns/include/dns/dnstap.h b/lib/dns/include/dns/dnstap.h index 8ec7162c6c5..e7bfd17b993 100644 --- a/lib/dns/include/dns/dnstap.h +++ b/lib/dns/include/dns/dnstap.h @@ -150,10 +150,16 @@ dns_dt_create(isc_mem_t *mctx, dns_dtmode_t mode, const char *path, */ isc_result_t -dns_dt_reopen(dns_dtenv_t *env); +dns_dt_reopen(dns_dtenv_t *env, int roll); /*%< * Reopens files established by dns_dt_create(). * + * If 'roll' is non-negative and 'env->mode' is dns_dtmode_file, + * then the file is automatically rolled over before reopening. + * The value of 'roll' indicates the number of backup log files to + * keep. If 'roll' is negative, or if 'env->mode' is dns_dtmode_unix, + * then the channel is simply reopened. + * * Requires: *\li 'env' is a valid dnstap environment. */ diff --git a/lib/dns/tests/dispatch_test.c b/lib/dns/tests/dispatch_test.c index f52a4c7fdd3..71445aa9121 100644 --- a/lib/dns/tests/dispatch_test.c +++ b/lib/dns/tests/dispatch_test.c @@ -139,11 +139,11 @@ ATF_TC_BODY(dispatchset_get, tc) { static void senddone(isc_task_t *task, isc_event_t *event) { - isc_socket_t *socket = event->ev_arg; + isc_socket_t *sock = event->ev_arg; UNUSED(task); - isc_socket_detach(&socket); + isc_socket_detach(&sock); isc_event_free(&event); } @@ -152,7 +152,7 @@ nameserver(isc_task_t *task, isc_event_t *event) { isc_result_t result; isc_region_t region; isc_socket_t *dummy; - isc_socket_t *socket = event->ev_arg; + isc_socket_t *sock = event->ev_arg; isc_socketevent_t *ev = (isc_socketevent_t *)event; static unsigned char buf1[16]; static unsigned char buf2[16]; @@ -171,8 +171,8 @@ nameserver(isc_task_t *task, isc_event_t *event) { region.base = buf1; region.length = sizeof(buf1); dummy = NULL; - isc_socket_attach(socket, &dummy); - result = isc_socket_sendto(socket, ®ion, task, senddone, socket, + isc_socket_attach(sock, &dummy); + result = isc_socket_sendto(sock, ®ion, task, senddone, sock, &ev->address, NULL); if (result != ISC_R_SUCCESS) isc_socket_detach(&dummy); @@ -183,8 +183,8 @@ nameserver(isc_task_t *task, isc_event_t *event) { region.base = buf2; region.length = sizeof(buf2); dummy = NULL; - isc_socket_attach(socket, &dummy); - result = isc_socket_sendto(socket, ®ion, task, senddone, socket, + isc_socket_attach(sock, &dummy); + result = isc_socket_sendto(sock, ®ion, task, senddone, sock, &ev->address, NULL); if (result != ISC_R_SUCCESS) isc_socket_detach(&dummy); @@ -222,8 +222,8 @@ ATF_TC_BODY(dispatch_getnext, tc) { isc_region_t region; isc_result_t result; isc_sockaddr_t local; - isc_socket_t *dsocket = NULL; - isc_socket_t *socket = NULL; + isc_socket_t *dsock = NULL; + isc_socket_t *sock = NULL; isc_task_t *task = NULL; isc_uint16_t id; struct in_addr ina; @@ -254,21 +254,21 @@ ATF_TC_BODY(dispatch_getnext, tc) { * Create a local udp nameserver on the loopback. */ result = isc_socket_create(socketmgr, AF_INET, isc_sockettype_udp, - &socket); + &sock); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); ina.s_addr = htonl(INADDR_LOOPBACK); isc_sockaddr_fromin(&local, &ina, 0); - result = isc_socket_bind(socket, &local, 0); + result = isc_socket_bind(sock, &local, 0); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); - result = isc_socket_getsockname(socket, &local); + result = isc_socket_getsockname(sock, &local); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); first = ISC_TRUE; region.base = rbuf; region.length = sizeof(rbuf); - result = isc_socket_recv(socket, ®ion, 1, task, nameserver, socket); + result = isc_socket_recv(sock, ®ion, 1, task, nameserver, sock); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); result = dns_dispatch_addresponse(dispatch, &local, task, response, @@ -279,10 +279,10 @@ ATF_TC_BODY(dispatch_getnext, tc) { message[0] = (id >> 8) & 0xff; message[1] = id & 0xff; - isc_socket_attach(dns_dispatch_getsocket(dispatch), &dsocket); + isc_socket_attach(dns_dispatch_getsocket(dispatch), &dsock); region.base = message; region.length = sizeof(message); - result = isc_socket_sendto(dsocket, ®ion, task, senddone, dsocket, + result = isc_socket_sendto(dsock, ®ion, task, senddone, dsock, &local, NULL); ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); @@ -294,8 +294,8 @@ ATF_TC_BODY(dispatch_getnext, tc) { /* * Shutdown nameserver. */ - isc_socket_cancel(socket, task, ISC_SOCKCANCEL_RECV); - isc_socket_detach(&socket); + isc_socket_cancel(sock, task, ISC_SOCKCANCEL_RECV); + isc_socket_detach(&sock); isc_task_detach(&task); /* diff --git a/lib/isc/include/isc/log.h b/lib/isc/include/isc/log.h index e3a8f81fb0b..7780abf3da8 100644 --- a/lib/isc/include/isc/log.h +++ b/lib/isc/include/isc/log.h @@ -903,6 +903,15 @@ isc_log_setcontext(isc_log_t *lctx); *\li lctx be a valid context. */ +isc_result_t +isc_logfile_roll(isc_logfile_t *file); +/*%< + * Roll a logfile. + * + * Requires: + *\li file is not NULL. + */ + ISC_LANG_ENDDECLS #endif /* ISC_LOG_H */ diff --git a/lib/isc/log.c b/lib/isc/log.c index 927c1b022ed..337a0d90d55 100644 --- a/lib/isc/log.c +++ b/lib/isc/log.c @@ -223,10 +223,7 @@ static isc_result_t sync_channellist(isc_logconfig_t *lcfg); static isc_result_t -greatest_version(isc_logchannel_t *channel, int *greatest); - -static isc_result_t -roll_log(isc_logchannel_t *channel); +greatest_version(isc_logfile_t *file, int *greatest); static void isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category, @@ -1134,8 +1131,7 @@ sync_channellist(isc_logconfig_t *lcfg) { } static isc_result_t -greatest_version(isc_logchannel_t *channel, int *greatestp) { - /* XXXDCL HIGHLY NT */ +greatest_version(isc_logfile_t *file, int *greatestp) { char *bname, *digit_end; const char *dirname; int version, greatest = -1; @@ -1147,15 +1143,13 @@ greatest_version(isc_logchannel_t *channel, int *greatestp) { char *bname2; #endif - REQUIRE(channel->type == ISC_LOG_TOFILE); - /* * It is safe to DE_CONST the file.name because it was copied - * with isc_mem_strdup in isc_log_createchannel. + * with isc_mem_strdup(). */ - bname = strrchr(FILE_NAME(channel), sep); + bname = strrchr(file->name, sep); #ifdef _WIN32 - bname2 = strrchr(FILE_NAME(channel), '\\'); + bname2 = strrchr(file->name, '\\'); if ((bname != NULL && bname2 != NULL && bname2 > bname) || (bname == NULL && bname2 != NULL)) { bname = bname2; @@ -1164,9 +1158,9 @@ greatest_version(isc_logchannel_t *channel, int *greatestp) { #endif if (bname != NULL) { *bname++ = '\0'; - dirname = FILE_NAME(channel); + dirname = file->name; } else { - DE_CONST(FILE_NAME(channel), bname); + DE_CONST(file->name, bname); dirname = "."; } bnamelen = strlen(bname); @@ -1177,7 +1171,7 @@ greatest_version(isc_logchannel_t *channel, int *greatestp) { /* * Replace the file separator if it was taken out. */ - if (bname != FILE_NAME(channel)) + if (bname != file->name) *(bname - 1) = sep; /* @@ -1189,8 +1183,8 @@ greatest_version(isc_logchannel_t *channel, int *greatestp) { while (isc_dir_read(&dir) == ISC_R_SUCCESS) { if (dir.entry.length > bnamelen && strncmp(dir.entry.name, bname, bnamelen) == 0 && - dir.entry.name[bnamelen] == '.') { - + dir.entry.name[bnamelen] == '.') + { version = strtol(&dir.entry.name[bnamelen + 1], &digit_end, 10); if (*digit_end == '\0' && version > greatest) @@ -1204,23 +1198,25 @@ greatest_version(isc_logchannel_t *channel, int *greatestp) { return (ISC_R_SUCCESS); } -static isc_result_t -roll_log(isc_logchannel_t *channel) { +isc_result_t +isc_logfile_roll(isc_logfile_t *file) { int i, n, greatest; char current[PATH_MAX + 1]; char new[PATH_MAX + 1]; const char *path; isc_result_t result; + REQUIRE(file != NULL); + /* * Do nothing (not even excess version trimming) if ISC_LOG_ROLLNEVER * is specified. Apparently complete external control over the log * files is desired. */ - if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER) + if (file->versions == ISC_LOG_ROLLNEVER) return (ISC_R_SUCCESS); - path = FILE_NAME(channel); + path = file->name; /* * Set greatest_version to the greatest existing version @@ -1228,30 +1224,29 @@ roll_log(isc_logchannel_t *channel) { * though the file names are 0 based, so an oldest log of log.1 * is a greatest_version of 2. */ - result = greatest_version(channel, &greatest); + result = greatest_version(file, &greatest); if (result != ISC_R_SUCCESS) return (result); /* * Now greatest should be set to the highest version number desired. - * Since the highest number is one less than FILE_VERSIONS(channel) + * Since the highest number is one less than file->versions. * when not doing infinite log rolling, greatest will need to be * decremented when it is equal to -- or greater than -- - * FILE_VERSIONS(channel). When greatest is less than - * FILE_VERSIONS(channel), it is already suitable for use as + * file->versions. When greatest is less than + * file->versions, it is already suitable for use as * the maximum version number. */ - if (FILE_VERSIONS(channel) == ISC_LOG_ROLLINFINITE || - FILE_VERSIONS(channel) > greatest) + if (file->versions == ISC_LOG_ROLLINFINITE || file->versions > greatest) ; /* Do nothing. */ else /* - * When greatest is >= FILE_VERSIONS(channel), it needs to - * be reduced until it is FILE_VERSIONS(channel) - 1. + * When greatest is >= file->versions, it needs to + * be reduced until it is file->versions - 1. * Remove any excess logs on the way to that value. */ - while (--greatest >= FILE_VERSIONS(channel)) { + while (--greatest >= file->versions) { n = snprintf(current, sizeof(current), "%s.%d", path, greatest); if (n >= (int)sizeof(current) || n < 0) @@ -1286,7 +1281,7 @@ roll_log(isc_logchannel_t *channel) { isc_result_totext(result)); } - if (FILE_VERSIONS(channel) != 0) { + if (file->versions != 0) { n = snprintf(new, sizeof(new), "%s.0", path); if (n >= (int)sizeof(new) || n < 0) result = ISC_R_NOSPACE; @@ -1348,11 +1343,11 @@ isc_log_open(isc_logchannel_t *channel) { if (result == ISC_R_SUCCESS && roll) { if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER) return (ISC_R_MAXSIZE); - result = roll_log(channel); + result = isc_logfile_roll(&channel->destination.file); if (result != ISC_R_SUCCESS) { if ((channel->flags & ISC_LOG_OPENERR) == 0) { syslog(LOG_ERR, - "isc_log_open: roll_log '%s' " + "isc_log_open: isc_logfile_roll '%s' " "failed: %s", FILE_NAME(channel), isc_result_totext(result)); diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index e134f220d7d..6ab8a9f1ec2 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -399,6 +399,7 @@ isc_logconfig_create isc_logconfig_destroy isc_logconfig_get isc_logconfig_use +isc_logfile_roll isc_md5_final isc_md5_init isc_md5_invalidate