+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]
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,
#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);
* 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 */
}
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
}
</varlistentry>
<varlistentry>
- <term><userinput>dnstap-reopen</userinput></term>
+ <term><userinput>dnstap ( -reopen | -roll <optional><replaceable>number</replaceable></optional> )</userinput></term>
<listitem>
<para>
- 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.
+ <command>rndc dnstap -reopen</command> allows the output
+ file to be renamed externally, then re-opened.
+ <command>rndc dnstap -roll</command> 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 <replaceable>number</replaceable> is specified, then the
+ number of backup log files is limited to that number.
</para>
</listitem>
</varlistentry>
</varlistentry>
<varlistentry>
- <term><userinput>recursing</userinput></term>
- <listitem>
- <para>
- Dump the list of queries <command>named</command> 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>fetches-per-zone</option> option.)
- </para>
- </listitem>
+ <term><userinput>recursing</userinput></term>
+ <listitem>
+ <para>
+ Dump the list of queries <command>named</command> 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>fetches-per-zone</option> option.)
+ </para>
+ </listitem>
</varlistentry>
<varlistentry>
$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
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`
a human-readable format.
</para>
<para>
- <command>rndc dnstap-reopen</command> can be used reopen
- dnstap output files after renaming them.
+ <command>rndc dnstap -roll</command> causes <command>dnstap</command>
+ output files to be rolled like log files -- the most recent output
+ file is renamed with a <filename>.0</filename> suffix, the next
+ most recent with <filename>.1</filename>, etc. (Note that this
+ only works when <command>dnstap</command> 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.
+ </para>
+ <para>
+ <command>rndc dnstap -reopen</command> simply closes and reopens
+ the <command>dnstap</command> output channel without renaming
+ the output file.
</para>
<para>
For more information on <command>dnstap</command>, see
isc_region_t identity;
isc_region_t version;
char *path;
+ dns_dtmode_t mode;
};
#define CHECK(x) do { \
fstrm_writer_destroy(&fw);
CHECK(ISC_R_FAILURE);
}
+ env->mode = mode;
isc_mem_attach(mctx, &env->mctx);
}
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
*/
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.
*/
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);
}
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];
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);
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);
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;
* 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,
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);
/*
* 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);
/*
*\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 */
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,
}
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;
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;
#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);
/*
* Replace the file separator if it was taken out.
*/
- if (bname != FILE_NAME(channel))
+ if (bname != file->name)
*(bname - 1) = sep;
/*
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)
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
* 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)
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;
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));
isc_logconfig_destroy
isc_logconfig_get
isc_logconfig_use
+isc_logfile_roll
isc_md5_final
isc_md5_init
isc_md5_invalidate