dnscap/src/dumper.c
Daniel Baumann 7fadabf04f
Merging upstream version 2.2.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-02-08 12:16:03 +01:00

395 lines
12 KiB
C

/*
* Copyright (c) 2016-2024 OARC, 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 "config.h"
#include "dumper.h"
#include "iaddr.h"
#include "log.h"
#include "pcaps.h"
/*
* when flags & DNSCAP_OUTPUT_ISDNS, payload points to a DNS packet
*/
void output(const char* descr, iaddr from, iaddr to, uint8_t proto, unsigned flags,
unsigned sport, unsigned dport, my_bpftimeval ts,
u_char* pkt_copy, const unsigned olen,
u_char* payload, const unsigned payloadlen)
{
struct plugin* p;
for (p = HEAD(plugins); p != NULL; p = NEXT(p, link)) {
if (p->filter && (*p->filter)(descr, &from, &to, proto, flags, sport, dport, ts, pkt_copy, olen, payload, payloadlen)) {
if (dumptrace >= 3) {
fprintf(stderr, "filtered: capturedbytes=%zu, proto=%d, isfrag=%s, isdns=%s, olen=%u, payloadlen=%u\n",
capturedbytes,
proto,
flags & DNSCAP_OUTPUT_ISFRAG ? "yes" : "no",
flags & DNSCAP_OUTPUT_ISDNS ? "yes" : "no",
olen,
payloadlen);
}
return;
}
}
msgcount++;
capturedbytes += olen;
if (dumptrace >= 3) {
fprintf(stderr, "output: capturedbytes=%zu, proto=%d, isfrag=%s, isdns=%s, olen=%u, payloadlen=%u\n",
capturedbytes,
proto,
flags & DNSCAP_OUTPUT_ISFRAG ? "yes" : "no",
flags & DNSCAP_OUTPUT_ISDNS ? "yes" : "no",
olen,
payloadlen);
}
/* Output stage. */
if (preso) {
fputs(descr, stderr);
if (flags & DNSCAP_OUTPUT_ISFRAG) {
fprintf(stderr, ";: [%s] ", ia_str(from));
fprintf(stderr, "-> [%s] (frag)\n", ia_str(to));
} else {
fprintf(stderr, "\t[%s].%u ", ia_str(from), sport);
fprintf(stderr, "[%s].%u ", ia_str(to), dport);
if ((flags & DNSCAP_OUTPUT_ISDNS) && payload)
dump_dns(payload, payloadlen, stderr, "\\\n\t");
}
putc('\n', stderr);
}
if (dump_type != nowhere) {
if (options.dump_format == pcap) {
struct pcap_pkthdr h;
memset(&h, 0, sizeof h);
h.ts = ts;
h.len = h.caplen = olen;
pcap_dump((u_char*)dumper, &h, pkt_copy);
if (flush)
pcap_dump_flush(dumper);
} else if (options.dump_format == cbor && (flags & DNSCAP_OUTPUT_ISDNS) && payload) {
int ret = output_cbor(from, to, proto, flags, sport, dport, ts, payload, payloadlen);
if (ret == DUMP_CBOR_FLUSH) {
if (dumper_close(ts)) {
fprintf(stderr, "%s: dumper_close() failed\n", ProgramName);
exit(1);
}
if (dumper_open(ts)) {
fprintf(stderr, "%s: dumper_open() failed\n", ProgramName);
exit(1);
}
} else if (ret != DUMP_CBOR_OK) {
fprintf(stderr, "%s: output to cbor failed [%u]\n", ProgramName, ret);
exit(1);
}
} else if (options.dump_format == cds) {
int ret = output_cds(from, to, proto, flags, sport, dport, ts, pkt_copy, olen, payload, payloadlen);
if (ret == DUMP_CDS_FLUSH) {
if (dumper_close(ts)) {
fprintf(stderr, "%s: dumper_close() failed\n", ProgramName);
exit(1);
}
if (dumper_open(ts)) {
fprintf(stderr, "%s: dumper_open() failed\n", ProgramName);
exit(1);
}
} else if (ret != DUMP_CDS_OK) {
fprintf(stderr, "%s: output to cds failed [%u]\n", ProgramName, ret);
exit(1);
}
}
}
for (p = HEAD(plugins); p != NULL; p = NEXT(p, link))
if (p->output)
(*p->output)(descr, from, to, proto, flags, sport, dport, ts, pkt_copy, olen, payload, payloadlen);
return;
}
int dumper_open(my_bpftimeval ts)
{
const char* t = NULL;
struct plugin* p;
assert(dump_state == dumper_closed);
while (ts.tv_usec >= MILLION) {
ts.tv_sec++;
ts.tv_usec -= MILLION;
}
if (limit_seconds != 0U)
next_interval = ts.tv_sec
- (ts.tv_sec % limit_seconds)
+ limit_seconds;
if (dump_type == to_stdout) {
t = "-";
} else if (dump_type == to_file) {
char sbuf[64];
struct tm tm;
gmtime_r((time_t*)&ts.tv_sec, &tm);
strftime(sbuf, 64, "%Y%m%d.%H%M%S", &tm);
if (asprintf(&dumpname, "%s.%s.%06lu%s",
dump_base, sbuf,
(u_long)ts.tv_usec, dump_suffix ? dump_suffix : "")
< 0
|| asprintf(&dumpnamepart, "%s.part", dumpname) < 0) {
logerr("asprintf: %s", strerror(errno));
return (TRUE);
}
t = dumpnamepart;
}
if (NULL != t) {
if (options.dump_format == pcap) {
dumper = dnscap_pcap_dump_open(pcap_dead, t);
if (dumper == NULL) {
logerr("pcap dump open: %s",
pcap_geterr(pcap_dead));
return (TRUE);
}
}
}
dumpstart = ts.tv_sec;
if (limit_seconds != 0U) {
struct timeval now;
u_int seconds;
time_t targ;
gettimeofday(&now, NULL);
while (now.tv_usec >= MILLION) {
now.tv_sec++;
now.tv_usec -= MILLION;
}
targ = (((now.tv_sec + (limit_seconds / 2))
/ limit_seconds)
+ 1)
* limit_seconds;
assert(targ > now.tv_sec);
seconds = targ - now.tv_sec;
if (next_interval == 0) {
alarm(seconds);
alarm_set = TRUE;
}
}
for (p = HEAD(plugins); p != NULL; p = NEXT(p, link)) {
int x;
if (!p->open)
continue;
x = (*p->open)(ts);
if (0 == x)
continue;
logerr("%s_open returned %d", p->name, x);
}
dump_state = dumper_opened;
return (FALSE);
}
int dumper_close(my_bpftimeval ts)
{
int ret = FALSE;
struct plugin* p;
assert(dump_state == dumper_opened);
if (print_pcap_stats)
do_pcap_stats();
if (alarm_set) {
alarm(0);
alarm_set = FALSE;
}
if (options.dump_format == pcap) {
if (dumper) {
pcap_dump_close(dumper);
dumper = FALSE;
}
} else if (options.dump_format == cbor) {
if (dump_type == to_stdout) {
ret = dump_cbor(stdout);
if (ret != DUMP_CBOR_OK) {
fprintf(stderr, "%s: output to cbor failed [%u]\n", ProgramName, ret);
exit(1);
}
} else if (dump_type == to_file) {
FILE* fp;
if (!(fp = fopen(dumpnamepart, "w"))) {
fprintf(stderr, "%s: fopen(%s) failed: %s\n", ProgramName, dumpnamepart, strerror(errno));
exit(1);
}
ret = dump_cbor(fp);
fclose(fp);
if (ret != DUMP_CBOR_OK) {
fprintf(stderr, "%s: output to cbor failed [%u]\n", ProgramName, ret);
exit(1);
}
}
} else if (options.dump_format == cds) {
if (dump_type == to_stdout) {
ret = dump_cds(stdout);
if (ret != DUMP_CDS_OK) {
fprintf(stderr, "%s: output to cds failed [%u]\n", ProgramName, ret);
exit(1);
}
} else if (dump_type == to_file) {
FILE* fp;
if (!(fp = fopen(dumpnamepart, "w"))) {
fprintf(stderr, "%s: fopen(%s) failed: %s\n", ProgramName, dumpnamepart, strerror(errno));
exit(1);
}
ret = dump_cds(fp);
fclose(fp);
if (ret != DUMP_CDS_OK) {
fprintf(stderr, "%s: output to cds failed [%u]\n", ProgramName, ret);
exit(1);
}
}
}
if (dump_type == to_stdout) {
assert(dumpname == NULL);
assert(dumpnamepart == NULL);
if (dumptrace >= 1)
fprintf(stderr, "%s: breaking\n", ProgramName);
ret = TRUE;
} else if (dump_type == to_file) {
char* cmd = NULL;
;
if (dumptrace >= 1)
fprintf(stderr, "%s: closing %s\n",
ProgramName, dumpname);
if (rename(dumpnamepart, dumpname)) {
logerr("rename: %s", strerror(errno));
return ret;
}
if (kick_cmd != NULL)
if (asprintf(&cmd, "%s %s &", kick_cmd, dumpname) < 0) {
logerr("asprintf: %s", strerror(errno));
cmd = NULL;
}
free(dumpnamepart);
dumpnamepart = NULL;
free(dumpname);
dumpname = NULL;
if (cmd != NULL) {
int x = system(cmd);
if (x)
logerr("system: \"%s\" returned %d", cmd, x);
free(cmd);
}
if (kick_cmd == NULL && options.dump_format != cbor && options.dump_format != cds)
ret = TRUE;
}
for (p = HEAD(plugins); p != NULL; p = NEXT(p, link)) {
int x;
if (!p->close)
continue;
x = (*p->close)();
if (x)
logerr("%s_close returned %d", p->name, x);
}
dump_state = dumper_closed;
return (ret);
}
#if HAVE_ZLIB_H
#if HAVE_FUNOPEN
static int
gzip_cookie_write(void* cookie, const char* buf, int size)
{
return gzwrite((gzFile)cookie, (voidpc)buf, (unsigned)size);
}
#elif HAVE_FOPENCOOKIE
static ssize_t
gzip_cookie_write(void* cookie, const char* buf, size_t size)
{
return gzwrite((gzFile)cookie, (voidpc)buf, (unsigned)size);
}
#endif
static int
gzip_cookie_close(void* cookie)
{
return gzclose((gzFile)cookie);
}
#endif /* HAVE_ZLIB_H */
pcap_dumper_t* dnscap_pcap_dump_open(pcap_t* pcap, const char* path)
{
#if HAVE_ZLIB_H
#if HAVE_GZOPEN
if (wantgzip) {
FILE* fp = NULL;
gzFile z = gzopen(path, "w");
if (z == NULL) {
perror("gzopen");
return NULL;
}
#if HAVE_FUNOPEN
fp = funopen(z, NULL, gzip_cookie_write, NULL, gzip_cookie_close);
if (fp == NULL) {
perror("funopen");
return NULL;
}
#elif HAVE_FOPENCOOKIE
{
static cookie_io_functions_t cookiefuncs = {
NULL, gzip_cookie_write, NULL, gzip_cookie_close
};
fp = fopencookie(z, "w", cookiefuncs);
if (fp == NULL) {
perror("fopencookie");
return NULL;
}
}
#endif
return pcap_dump_fopen(pcap, fp);
}
#endif /* HAVE_GZOPEN */
#endif /* HAVE_ZLIB_H */
return pcap_dump_open(pcap, path);
}