diff options
Diffstat (limited to 'plugins/pcapdump')
-rw-r--r-- | plugins/pcapdump/Makefile.am | 22 | ||||
-rw-r--r-- | plugins/pcapdump/pcapdump.c | 262 | ||||
-rwxr-xr-x | plugins/pcapdump/test1.sh | 16 |
3 files changed, 300 insertions, 0 deletions
diff --git a/plugins/pcapdump/Makefile.am b/plugins/pcapdump/Makefile.am new file mode 100644 index 0000000..bfbf8a4 --- /dev/null +++ b/plugins/pcapdump/Makefile.am @@ -0,0 +1,22 @@ +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in +CLEANFILES = *.gcda *.gcno *.gcov + +AM_CFLAGS = -I$(srcdir) \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/isc \ + $(SECCOMPFLAGS) + +pkglib_LTLIBRARIES = pcapdump.la +pcapdump_la_SOURCES = pcapdump.c +pcapdump_la_LDFLAGS = -module -avoid-version + +TESTS = test1.sh +EXTRA_DIST = $(TESTS) +CLEANFILES += test1.out* *.pcap-dist + +if ENABLE_GCOV +gcov-local: + for src in $(pcapdump_la_SOURCES); do \ + gcov -o .libs -l -r -s "$(srcdir)" "$$src"; \ + done +endif diff --git a/plugins/pcapdump/pcapdump.c b/plugins/pcapdump/pcapdump.c new file mode 100644 index 0000000..ff70fe3 --- /dev/null +++ b/plugins/pcapdump/pcapdump.c @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2016-2021, 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" + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <pcap.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include <sys/wait.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#if HAVE_ARPA_NAMESER_COMPAT_H +#include <arpa/nameser_compat.h> +#endif + +#include "dnscap_common.h" + +#define SNAPLEN 65536 +#define THOUSAND 1000 +#define MILLION (THOUSAND * THOUSAND) + +output_t pcapdump_output; + +static logerr_t* logerr = 0; +char* dump_base = 0; +static int to_stdout = 0; +static int dbg_lvl = 0; +static char* dumpname = 0; +static char* dumpnamepart = 0; +static pcap_t* pcap_dead = 0; +static pcap_dumper_t* dumper = 0; +static char* kick_cmd = 0; +static int flush = 0; +static int dir_wanted = DIR_INITIATE | DIR_RESPONSE; + +void pcapdump_usage() +{ + fprintf(stderr, + "\npcapdump.so options:\n" + "\t-? print these instructions and exit\n" + "\t-d increase debugging\n" + "\t-f flush output on every packet\n" + "\t-k <cmd> kick off <cmd> when each dump closes\n" + "\t-s [ir] select sides: initiations, responses\n" + "\t-w <base> dump to <base>.<timesec>.<timeusec>\n"); +} + +void pcapdump_getopt(int* argc, char** argv[]) +{ + int c; + int u; + const char* p; + while ((c = getopt(*argc, *argv, "?dfk:s:w:")) != EOF) { + switch (c) { + case 'd': + dbg_lvl++; + break; + case 'f': + flush = 1; + break; + case 'k': + if (kick_cmd) + free(kick_cmd); + kick_cmd = strdup(optarg); + break; + case 's': + u = 0; + for (p = optarg; *p; p++) + switch (*p) { + case 'i': + u |= DIR_INITIATE; + break; + case 'r': + u |= DIR_RESPONSE; + break; + default: + fprintf(stderr, "-s takes only [ir]\n"); + pcapdump_usage(); + break; + } + dir_wanted = u; + break; + case 'w': + if (!strcmp(optarg, "-")) + to_stdout = 1; + else { + if (dump_base) + free(dump_base); + dump_base = strdup(optarg); + } + break; + case '?': + pcapdump_usage(); + if (!optopt || optopt == '?') { + exit(0); + } + // fallthrough + default: + exit(1); + } + } + if (!to_stdout && !dump_base) { + fprintf(stderr, "-w basename argument is required\n"); + pcapdump_usage(); + exit(1); + } + if (to_stdout && kick_cmd) { + fprintf(stderr, "Can't use -k when dumping to stdout\n"); + pcapdump_usage(); + exit(1); + } +} + +int pcapdump_start(logerr_t* a_logerr) +{ + logerr = a_logerr; + pcap_dead = pcap_open_dead(DLT_RAW, SNAPLEN); + return 0; +} + +void pcapdump_stop() +{ + pcap_close(pcap_dead); + pcap_dead = 0; +} + +int pcapdump_open(my_bpftimeval ts) +{ + const char* t = NULL; + if (to_stdout) { + t = "-"; + } else { + char sbuf[64]; + struct tm tm; + while (ts.tv_usec >= MILLION) { + ts.tv_sec++; + ts.tv_usec -= MILLION; + } + gmtime_r((time_t*)&ts.tv_sec, &tm); + strftime(sbuf, 64, "%Y%m%d.%H%M%S", &tm); + if (asprintf(&dumpname, "%s.%s.%06lu", + dump_base, sbuf, (u_long)ts.tv_usec) + < 0 + || asprintf(&dumpnamepart, "%s.part", dumpname) < 0) { + logerr("asprintf: %s", strerror(errno)); + return 1; + } + t = dumpnamepart; + } + dumper = pcap_dump_open(pcap_dead, t); + if (dumper == NULL) { + logerr("pcap dump open: %s", pcap_geterr(pcap_dead)); + return 1; + } + return 0; +} + +int pcapdump_close(my_bpftimeval ts) +{ + int ret = 0; +#if 0 + if (print_pcap_stats) + do_pcap_stats(); +#endif + pcap_dump_close(dumper); + dumper = 0; + if (to_stdout) { + assert(dumpname == 0); + assert(dumpnamepart == 0); + if (dbg_lvl >= 1) + logerr("breaking"); + ret = 0; + } else { + char* cmd = NULL; + if (dbg_lvl >= 1) + logerr("closing %s", dumpname); + if (rename(dumpnamepart, dumpname)) { + logerr("rename: %s", strerror(errno)); + return 1; + } + 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) + ret = 0; + } + return ret; +} + +void pcapdump_output(const char* descr, iaddr from, iaddr to, uint8_t proto, unsigned flags, + unsigned sport, unsigned dport, my_bpftimeval ts, + const u_char* pkt_copy, const unsigned olen, const u_char* payload, const unsigned payloadlen) +{ + struct pcap_pkthdr h; + if (flags & DNSCAP_OUTPUT_ISLAYER) + return; + if (flags & DNSCAP_OUTPUT_ISDNS) { + HEADER* dns = (HEADER*)payload; + if (0 == dns->qr && 0 == (dir_wanted & DIR_INITIATE)) + return; + if (1 == dns->qr && 0 == (dir_wanted & DIR_RESPONSE)) + return; + } + 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); +} diff --git a/plugins/pcapdump/test1.sh b/plugins/pcapdump/test1.sh new file mode 100755 index 0000000..bcf2fdb --- /dev/null +++ b/plugins/pcapdump/test1.sh @@ -0,0 +1,16 @@ +#!/bin/sh -xe + +plugin=`find . -name 'pcapdump.so' | head -n 1` +if [ -z "$plugin" ]; then + echo "Unable to find the pcapdump plugin" + exit 1 +fi + +ln -fs "$srcdir/../../src/test/dns.pcap" dns.pcap-dist + +../../src/dnscap -r dns.pcap-dist -g -P "$plugin" -? +../../src/dnscap -r dns.pcap-dist -g -P "$plugin" -dddd -w test1.out +../../src/dnscap -r dns.pcap-dist -g -P "$plugin" -dddd -f -w test1.out +../../src/dnscap -r dns.pcap-dist -g -P "$plugin" -dddd -s r -w test1.out +../../src/dnscap -r dns.pcap-dist -g -P "$plugin" -dddd -s i -w test1.out +! ../../src/dnscap -r dns.pcap-dist -g -P "$plugin" -X |