summaryrefslogtreecommitdiffstats
path: root/plugins/pcapdump/pcapdump.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/pcapdump/pcapdump.c')
-rw-r--r--plugins/pcapdump/pcapdump.c262
1 files changed, 262 insertions, 0 deletions
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);
+}