summaryrefslogtreecommitdiffstats
path: root/src/input
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2021-03-13 07:54:12 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2021-03-13 07:54:12 +0000
commit4754ed45b607e82450a5e31fea1da3ba61433b04 (patch)
tree3554490bdc003e6004f605abe41929cdf98b0651 /src/input
parentInitial commit. (diff)
downloaddnsjit-4754ed45b607e82450a5e31fea1da3ba61433b04.tar.xz
dnsjit-4754ed45b607e82450a5e31fea1da3ba61433b04.zip
Adding upstream version 1.1.0+debian.upstream/1.1.0+debian
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/input')
-rw-r--r--src/input/fpcap.c338
-rw-r--r--src/input/fpcap.h31
-rw-r--r--src/input/fpcap.hh62
-rw-r--r--src/input/fpcap.lua131
-rw-r--r--src/input/mmpcap.c335
-rw-r--r--src/input/mmpcap.h31
-rw-r--r--src/input/mmpcap.hh60
-rw-r--r--src/input/mmpcap.lua120
-rw-r--r--src/input/pcap.c206
-rw-r--r--src/input/pcap.h33
-rw-r--r--src/input/pcap.hh56
-rw-r--r--src/input/pcap.lua130
-rw-r--r--src/input/zero.c75
-rw-r--r--src/input/zero.h32
-rw-r--r--src/input/zero.hh37
-rw-r--r--src/input/zero.lua75
16 files changed, 1752 insertions, 0 deletions
diff --git a/src/input/fpcap.c b/src/input/fpcap.c
new file mode 100644
index 0000000..de054ec
--- /dev/null
+++ b/src/input/fpcap.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "input/fpcap.h"
+#include "core/assert.h"
+#include "core/object/pcap.h"
+
+#include <stdio.h>
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#else
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#else
+#ifdef HAVE_MACHINE_ENDIAN_H
+#include <machine/endian.h>
+#endif
+#endif
+#endif
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#endif
+#ifndef bswap_16
+#ifndef bswap16
+#define bswap_16(x) swap16(x)
+#define bswap_32(x) swap32(x)
+#define bswap_64(x) swap64(x)
+#else
+#define bswap_16(x) bswap16(x)
+#define bswap_32(x) bswap32(x)
+#define bswap_64(x) bswap64(x)
+#endif
+#endif
+#include <pcap/pcap.h>
+
+#define MAX_SNAPLEN 0x40000
+
+static core_log_t _log = LOG_T_INIT("input.fpcap");
+static input_fpcap_t _defaults = {
+ LOG_T_INIT_OBJ("input.fpcap"),
+ 0, 0,
+ 0, 0, 0,
+ CORE_OBJECT_PCAP_INIT(0),
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,
+ 0
+};
+
+core_log_t* input_fpcap_log()
+{
+ return &_log;
+}
+
+void input_fpcap_init(input_fpcap_t* self)
+{
+ mlassert_self();
+
+ *self = _defaults;
+}
+
+void input_fpcap_destroy(input_fpcap_t* self)
+{
+ mlassert_self();
+
+ if (!self->extern_file && self->file) {
+ fclose(self->file);
+ }
+ free(self->buf);
+}
+
+static int _open(input_fpcap_t* self)
+{
+ mlassert_self();
+
+ if (fread(&self->magic_number, 1, 4, self->file) != 4
+ || fread(&self->version_major, 1, 2, self->file) != 2
+ || fread(&self->version_minor, 1, 2, self->file) != 2
+ || fread(&self->thiszone, 1, 4, self->file) != 4
+ || fread(&self->sigfigs, 1, 4, self->file) != 4
+ || fread(&self->snaplen, 1, 4, self->file) != 4
+ || fread(&self->network, 1, 4, self->file) != 4) {
+ lcritical("could not read full PCAP header");
+ return -2;
+ }
+ switch (self->magic_number) {
+ case 0x4d3cb2a1:
+ self->is_nanosec = 1;
+ case 0xd4c3b2a1:
+ self->is_swapped = 1;
+ self->version_major = bswap_16(self->version_major);
+ self->version_minor = bswap_16(self->version_minor);
+ self->thiszone = (int32_t)bswap_32((uint32_t)self->thiszone);
+ self->sigfigs = bswap_32(self->sigfigs);
+ self->snaplen = bswap_32(self->snaplen);
+ self->network = bswap_32(self->network);
+ break;
+ case 0xa1b2c3d4:
+ case 0xa1b23c4d:
+ break;
+ default:
+ lcritical("invalid PCAP header");
+ return -2;
+ }
+
+ if (self->snaplen > MAX_SNAPLEN) {
+ lcritical("too large snaplen (%u)", self->snaplen);
+ return -2;
+ }
+
+ if (self->version_major != 2 || self->version_minor != 4) {
+ lcritical("unsupported PCAP version v%u.%u", self->version_major, self->version_minor);
+ return -2;
+ }
+
+ /*
+ * Translation taken from https://github.com/the-tcpdump-group/libpcap/blob/90543970fd5fbed261d3637f5ec4811d7dde4e49/pcap-common.c#L1212 .
+ */
+ switch (self->network) {
+ case 101:
+ self->linktype = DLT_RAW;
+ break;
+#ifdef DLT_FR
+ case 107: /* LINKTYPE_FRELAY */
+ self->linktype = DLT_FR;
+ break;
+#endif
+ case 100: /* LINKTYPE_ATM_RFC1483 */
+ self->linktype = DLT_ATM_RFC1483;
+ break;
+ case 102: /* LINKTYPE_SLIP_BSDOS */
+ self->linktype = DLT_SLIP_BSDOS;
+ break;
+ case 103: /* LINKTYPE_PPP_BSDOS */
+ self->linktype = DLT_PPP_BSDOS;
+ break;
+ case 104: /* LINKTYPE_C_HDLC */
+ self->linktype = DLT_C_HDLC;
+ break;
+ case 106: /* LINKTYPE_ATM_CLIP */
+ self->linktype = DLT_ATM_CLIP;
+ break;
+ case 50: /* LINKTYPE_PPP_HDLC */
+ self->linktype = DLT_PPP_SERIAL;
+ break;
+ case 51: /* LINKTYPE_PPP_ETHER */
+ self->linktype = DLT_PPP_ETHER;
+ break;
+ default:
+ self->linktype = self->network;
+ }
+
+ lfatal_oom(self->buf = malloc(self->snaplen));
+ self->prod_pkt.snaplen = self->snaplen;
+ self->prod_pkt.linktype = self->linktype;
+ self->prod_pkt.bytes = (unsigned char*)self->buf;
+ self->prod_pkt.is_swapped = self->is_swapped;
+
+ ldebug("pcap v%u.%u snaplen:%lu %s", self->version_major, self->version_minor, self->snaplen, self->is_swapped ? " swapped" : "");
+
+ return 0;
+}
+
+int input_fpcap_open(input_fpcap_t* self, const char* file)
+{
+ mlassert_self();
+ lassert(file, "file is nil");
+
+ if (self->file) {
+ lfatal("already opened");
+ }
+
+ if (!(self->file = fopen(file, "rb"))) {
+ lcritical("fopen(%s) error: %s", file, core_log_errstr(errno));
+ return -1;
+ }
+
+ return _open(self);
+}
+
+int input_fpcap_openfp(input_fpcap_t* self, void* fp)
+{
+ mlassert_self();
+
+ if (self->file) {
+ lfatal("already opened");
+ }
+
+ self->file = fp;
+ self->extern_file = 1;
+
+ return _open(self);
+}
+
+int input_fpcap_run(input_fpcap_t* self)
+{
+ struct {
+ uint32_t ts_sec;
+ uint32_t ts_usec;
+ uint32_t incl_len;
+ uint32_t orig_len;
+ } hdr;
+ core_object_pcap_t pkt = CORE_OBJECT_PCAP_INIT(0);
+ int ret;
+ mlassert_self();
+
+ if (!self->file) {
+ lfatal("no PCAP opened");
+ }
+ if (!self->recv) {
+ lfatal("no receiver set");
+ }
+
+ pkt.snaplen = self->snaplen;
+ pkt.linktype = self->linktype;
+ pkt.bytes = (unsigned char*)self->buf;
+ pkt.is_swapped = self->is_swapped;
+
+ while ((ret = fread(&hdr, 1, 16, self->file)) == 16) {
+ if (self->is_swapped) {
+ hdr.ts_sec = bswap_32(hdr.ts_sec);
+ hdr.ts_usec = bswap_32(hdr.ts_usec);
+ hdr.incl_len = bswap_32(hdr.incl_len);
+ hdr.orig_len = bswap_32(hdr.orig_len);
+ }
+ if (hdr.incl_len > self->snaplen) {
+ lwarning("invalid packet length, larger then snaplen");
+ return -1;
+ }
+ if (fread(self->buf, 1, hdr.incl_len, self->file) != hdr.incl_len) {
+ lwarning("could not read all of packet, aborting");
+ return -1;
+ }
+
+ self->pkts++;
+
+ pkt.ts.sec = hdr.ts_sec;
+ if (self->is_nanosec) {
+ pkt.ts.nsec = hdr.ts_usec;
+ } else {
+ pkt.ts.nsec = hdr.ts_usec * 1000;
+ }
+ pkt.caplen = hdr.incl_len;
+ pkt.len = hdr.orig_len;
+
+ self->recv(self->ctx, (core_object_t*)&pkt);
+ }
+ if (ret) {
+ lwarning("could not read next PCAP header, aborting");
+ return -1;
+ }
+
+ return 0;
+}
+
+static const core_object_t* _produce(input_fpcap_t* self)
+{
+ struct {
+ uint32_t ts_sec;
+ uint32_t ts_usec;
+ uint32_t incl_len;
+ uint32_t orig_len;
+ } hdr;
+ int ret;
+ mlassert_self();
+
+ if (self->is_broken) {
+ lwarning("PCAP is broken, will not read next packet");
+ return 0;
+ }
+
+ if ((ret = fread(&hdr, 1, 16, self->file)) != 16) {
+ if (ret) {
+ lwarning("could not read next PCAP header, aborting");
+ self->is_broken = 1;
+ }
+ return 0;
+ }
+
+ if (self->is_swapped) {
+ hdr.ts_sec = bswap_32(hdr.ts_sec);
+ hdr.ts_usec = bswap_32(hdr.ts_usec);
+ hdr.incl_len = bswap_32(hdr.incl_len);
+ hdr.orig_len = bswap_32(hdr.orig_len);
+ }
+ if (hdr.incl_len > self->snaplen) {
+ lwarning("invalid packet length, larger then snaplen");
+ self->is_broken = 1;
+ return 0;
+ }
+ if (fread(self->buf, 1, hdr.incl_len, self->file) != hdr.incl_len) {
+ lwarning("could not read all of packet, aborting");
+ self->is_broken = 1;
+ return 0;
+ }
+
+ self->pkts++;
+
+ self->prod_pkt.ts.sec = hdr.ts_sec;
+ if (self->is_nanosec) {
+ self->prod_pkt.ts.nsec = hdr.ts_usec;
+ } else {
+ self->prod_pkt.ts.nsec = hdr.ts_usec * 1000;
+ }
+ self->prod_pkt.caplen = hdr.incl_len;
+ self->prod_pkt.len = hdr.orig_len;
+
+ return (core_object_t*)&self->prod_pkt;
+}
+
+core_producer_t input_fpcap_producer(input_fpcap_t* self)
+{
+ mlassert_self();
+
+ if (!self->file) {
+ lfatal("no PCAP opened");
+ }
+
+ return (core_producer_t)_produce;
+}
diff --git a/src/input/fpcap.h b/src/input/fpcap.h
new file mode 100644
index 0000000..da3596c
--- /dev/null
+++ b/src/input/fpcap.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "core/log.h"
+#include "core/receiver.h"
+#include "core/producer.h"
+#include "core/object/pcap.h"
+
+#ifndef __dnsjit_input_fpcap_h
+#define __dnsjit_input_fpcap_h
+
+#include "input/fpcap.hh"
+
+#endif
diff --git a/src/input/fpcap.hh b/src/input/fpcap.hh
new file mode 100644
index 0000000..0ced83c
--- /dev/null
+++ b/src/input/fpcap.hh
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//lua:require("dnsjit.core.log")
+//lua:require("dnsjit.core.receiver_h")
+//lua:require("dnsjit.core.producer_h")
+//lua:require("dnsjit.core.object.pcap_h")
+
+typedef struct input_fpcap {
+ core_log_t _log;
+ core_receiver_t recv;
+ void* ctx;
+
+ uint8_t is_swapped;
+ uint8_t is_nanosec;
+ uint8_t is_broken;
+
+ core_object_pcap_t prod_pkt;
+
+ void* file;
+ int extern_file;
+ size_t pkts;
+ uint8_t* buf;
+ size_t buf_size;
+
+ uint32_t magic_number;
+ uint16_t version_major;
+ uint16_t version_minor;
+ int32_t thiszone;
+ uint32_t sigfigs;
+ uint32_t snaplen;
+ uint32_t network;
+
+ uint32_t linktype;
+} input_fpcap_t;
+
+core_log_t* input_fpcap_log();
+
+void input_fpcap_init(input_fpcap_t* self);
+void input_fpcap_destroy(input_fpcap_t* self);
+int input_fpcap_open(input_fpcap_t* self, const char* file);
+int input_fpcap_openfp(input_fpcap_t* self, void* fp);
+int input_fpcap_run(input_fpcap_t* self);
+
+core_producer_t input_fpcap_producer(input_fpcap_t* self);
diff --git a/src/input/fpcap.lua b/src/input/fpcap.lua
new file mode 100644
index 0000000..2fc2906
--- /dev/null
+++ b/src/input/fpcap.lua
@@ -0,0 +1,131 @@
+-- Copyright (c) 2018-2021, OARC, Inc.
+-- All rights reserved.
+--
+-- This file is part of dnsjit.
+--
+-- dnsjit is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- dnsjit is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+
+-- dnsjit.input.fpcap
+-- Read input from a PCAP file using fopen()
+-- local input = require("dnsjit.input.fpcap").new()
+-- input:open("file.pcap")
+-- input:receiver(filter_or_output)
+-- input:run()
+--
+-- Read input from a PCAP file using standard library function
+-- .B fopen()
+-- and parse the PCAP without libpcap.
+-- After opening a file and reading the PCAP header, the attributes are
+-- populated.
+-- .SS Attributes
+-- .TP
+-- is_swapped
+-- Indicate if the byte order in the PCAP is in reverse order of the host.
+-- .TP
+-- is_nanosec
+-- Indicate if the time stamps are in nanoseconds or not.
+-- .TP
+-- magic_number
+-- Magic number.
+-- .TP
+-- version_major
+-- Major version number.
+-- .TP
+-- version_minor
+-- Minor version number.
+-- .TP
+-- thiszone
+-- GMT to local correction.
+-- .TP
+-- sigfigs
+-- Accuracy of timestamps.
+-- .TP
+-- snaplen
+-- Max length of captured packets, in octets.
+-- .TP
+-- network
+-- The link type found in the PCAP header, see https://www.tcpdump.org/linktypes.html .
+-- .TP
+-- linktype
+-- The data link type, mapped from
+-- .IR network .
+module(...,package.seeall)
+
+require("dnsjit.input.fpcap_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "input_fpcap_t"
+local input_fpcap_t = ffi.typeof(t_name)
+local Fpcap = {}
+
+-- Create a new Fpcap input.
+function Fpcap.new()
+ local self = {
+ _receiver = nil,
+ obj = input_fpcap_t(),
+ }
+ C.input_fpcap_init(self.obj)
+ ffi.gc(self.obj, C.input_fpcap_destroy)
+ return setmetatable(self, { __index = Fpcap })
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Fpcap:log()
+ if self == nil then
+ return C.input_fpcap_log()
+ end
+ return self.obj._log
+end
+
+-- Set the receiver to pass objects to.
+function Fpcap:receiver(o)
+ self.obj.recv, self.obj.ctx = o:receive()
+ self._receiver = o
+end
+
+-- Return the C functions and context for producing objects.
+function Fpcap:produce()
+ return C.input_fpcap_producer(self.obj), self.obj
+end
+
+-- Open a PCAP file for processing and read the PCAP header.
+-- Returns 0 on success.
+function Fpcap:open(file)
+ return C.input_fpcap_open(self.obj, file)
+end
+
+-- Open a PCAP file for processing and read the PCAP header using a
+-- file descriptor, for example
+-- .B io.stdin
+-- or with
+-- .BR io.open() .
+-- Will not take ownership of the file descriptor.
+-- Returns 0 on success.
+function Fpcap:openfp(fp)
+ return C.input_fpcap_openfp(self.obj, fp)
+end
+
+-- Start processing packets and send each packet read to the receiver.
+-- Returns 0 if all packets was read successfully.
+function Fpcap:run()
+ return C.input_fpcap_run(self.obj)
+end
+
+-- Return the number of packets seen.
+function Fpcap:packets()
+ return tonumber(self.obj.pkts)
+end
+
+return Fpcap
diff --git a/src/input/mmpcap.c b/src/input/mmpcap.c
new file mode 100644
index 0000000..4f3ee59
--- /dev/null
+++ b/src/input/mmpcap.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "input/mmpcap.h"
+#include "core/assert.h"
+#include "core/object/pcap.h"
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#else
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#else
+#ifdef HAVE_MACHINE_ENDIAN_H
+#include <machine/endian.h>
+#endif
+#endif
+#endif
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#endif
+#ifndef bswap_16
+#ifndef bswap16
+#define bswap_16(x) swap16(x)
+#define bswap_32(x) swap32(x)
+#define bswap_64(x) swap64(x)
+#else
+#define bswap_16(x) bswap16(x)
+#define bswap_32(x) bswap32(x)
+#define bswap_64(x) bswap64(x)
+#endif
+#endif
+#include <pcap/pcap.h>
+
+static core_log_t _log = LOG_T_INIT("input.mmpcap");
+static input_mmpcap_t _defaults = {
+ LOG_T_INIT_OBJ("input.mmpcap"),
+ 0, 0,
+ 0, 0, 0,
+ CORE_OBJECT_PCAP_INIT(0),
+ -1, 0, 0, 0, MAP_FAILED,
+ 0, 0, 0, 0, 0, 0, 0,
+ 0
+};
+
+core_log_t* input_mmpcap_log()
+{
+ return &_log;
+}
+
+void input_mmpcap_init(input_mmpcap_t* self)
+{
+ mlassert_self();
+
+ *self = _defaults;
+}
+
+void input_mmpcap_destroy(input_mmpcap_t* self)
+{
+ mlassert_self();
+
+ if (self->buf != MAP_FAILED) {
+ munmap(self->buf, self->len);
+ }
+ if (self->fd > -1) {
+ close(self->fd);
+ }
+}
+
+int input_mmpcap_open(input_mmpcap_t* self, const char* file)
+{
+ struct stat sb;
+ mlassert_self();
+ lassert(file, "file is nil");
+
+ if (self->fd != -1) {
+ lfatal("already opened");
+ }
+
+ if ((self->fd = open(file, O_RDONLY)) < 0) {
+ lcritical("open(%s) error %s", file, core_log_errstr(errno));
+ return -1;
+ }
+
+ if (fstat(self->fd, &sb)) {
+ lcritical("stat(%s) error %s", file, core_log_errstr(errno));
+ return -1;
+ }
+ self->len = sb.st_size;
+
+ if ((self->buf = mmap(0, self->len, PROT_READ, MAP_PRIVATE, self->fd, 0)) == MAP_FAILED) {
+ lcritical("mmap(%s) error %s", file, core_log_errstr(errno));
+ return -1;
+ }
+
+ if (self->len < 24) {
+ lcritical("could not read full PCAP header");
+ return -2;
+ }
+ memcpy(&self->magic_number, self->buf, 4);
+ memcpy(&self->version_major, self->buf + 4, 2);
+ memcpy(&self->version_minor, self->buf + 6, 2);
+ memcpy(&self->thiszone, self->buf + 8, 4);
+ memcpy(&self->sigfigs, self->buf + 12, 4);
+ memcpy(&self->snaplen, self->buf + 16, 4);
+ memcpy(&self->network, self->buf + 20, 4);
+ self->at = 24;
+ switch (self->magic_number) {
+ case 0x4d3cb2a1:
+ self->is_nanosec = 1;
+ case 0xd4c3b2a1:
+ self->is_swapped = 1;
+ self->version_major = bswap_16(self->version_major);
+ self->version_minor = bswap_16(self->version_minor);
+ self->thiszone = (int32_t)bswap_32((uint32_t)self->thiszone);
+ self->sigfigs = bswap_32(self->sigfigs);
+ self->snaplen = bswap_32(self->snaplen);
+ self->network = bswap_32(self->network);
+ break;
+ case 0xa1b2c3d4:
+ case 0xa1b23c4d:
+ break;
+ default:
+ lcritical("invalid PCAP header");
+ return -2;
+ }
+
+ if (self->version_major != 2 || self->version_minor != 4) {
+ lcritical("unsupported PCAP version v%u.%u", self->version_major, self->version_minor);
+ return -2;
+ }
+
+ /*
+ * Translation taken from https://github.com/the-tcpdump-group/libpcap/blob/90543970fd5fbed261d3637f5ec4811d7dde4e49/pcap-common.c#L1212 .
+ */
+ switch (self->network) {
+ case 101: /* LINKTYPE_RAW */
+ self->linktype = DLT_RAW;
+ break;
+#ifdef DLT_FR
+ case 107: /* LINKTYPE_FRELAY */
+ self->linktype = DLT_FR;
+ break;
+#endif
+ case 100: /* LINKTYPE_ATM_RFC1483 */
+ self->linktype = DLT_ATM_RFC1483;
+ break;
+ case 102: /* LINKTYPE_SLIP_BSDOS */
+ self->linktype = DLT_SLIP_BSDOS;
+ break;
+ case 103: /* LINKTYPE_PPP_BSDOS */
+ self->linktype = DLT_PPP_BSDOS;
+ break;
+ case 104: /* LINKTYPE_C_HDLC */
+ self->linktype = DLT_C_HDLC;
+ break;
+ case 106: /* LINKTYPE_ATM_CLIP */
+ self->linktype = DLT_ATM_CLIP;
+ break;
+ case 50: /* LINKTYPE_PPP_HDLC */
+ self->linktype = DLT_PPP_SERIAL;
+ break;
+ case 51: /* LINKTYPE_PPP_ETHER */
+ self->linktype = DLT_PPP_ETHER;
+ break;
+ default:
+ self->linktype = self->network;
+ }
+
+ self->prod_pkt.snaplen = self->snaplen;
+ self->prod_pkt.linktype = self->linktype;
+ self->prod_pkt.is_swapped = self->is_swapped;
+
+ ldebug("pcap v%u.%u snaplen:%lu %s", self->version_major, self->version_minor, self->snaplen, self->is_swapped ? " swapped" : "");
+
+ return 0;
+}
+
+int input_mmpcap_run(input_mmpcap_t* self)
+{
+ struct {
+ uint32_t ts_sec;
+ uint32_t ts_usec;
+ uint32_t incl_len;
+ uint32_t orig_len;
+ } hdr;
+ core_object_pcap_t pkt = CORE_OBJECT_PCAP_INIT(0);
+ mlassert_self();
+
+ if (self->buf == MAP_FAILED) {
+ lfatal("no PCAP opened");
+ }
+ if (!self->recv) {
+ lfatal("no receiver set");
+ }
+
+ pkt.snaplen = self->snaplen;
+ pkt.linktype = self->linktype;
+ pkt.is_swapped = self->is_swapped;
+
+ while (self->len - self->at > 16) {
+ memcpy(&hdr, &self->buf[self->at], 16);
+ self->at += 16;
+ if (self->is_swapped) {
+ hdr.ts_sec = bswap_32(hdr.ts_sec);
+ hdr.ts_usec = bswap_32(hdr.ts_usec);
+ hdr.incl_len = bswap_32(hdr.incl_len);
+ hdr.orig_len = bswap_32(hdr.orig_len);
+ }
+ if (hdr.incl_len > self->snaplen) {
+ lwarning("invalid packet length, larger then snaplen");
+ return -1;
+ }
+ if (self->len - self->at < hdr.incl_len) {
+ lwarning("could not read all of packet, aborting");
+ return -1;
+ }
+
+ self->pkts++;
+
+ pkt.ts.sec = hdr.ts_sec;
+ if (self->is_nanosec) {
+ pkt.ts.nsec = hdr.ts_usec;
+ } else {
+ pkt.ts.nsec = hdr.ts_usec * 1000;
+ }
+ pkt.bytes = (unsigned char*)&self->buf[self->at];
+ pkt.caplen = hdr.incl_len;
+ pkt.len = hdr.orig_len;
+
+ self->recv(self->ctx, (core_object_t*)&pkt);
+
+ self->at += hdr.incl_len;
+ }
+ if (self->at < self->len) {
+ lwarning("could not read next PCAP header, aborting");
+ return -1;
+ }
+
+ return 0;
+}
+
+static const core_object_t* _produce(input_mmpcap_t* self)
+{
+ struct {
+ uint32_t ts_sec;
+ uint32_t ts_usec;
+ uint32_t incl_len;
+ uint32_t orig_len;
+ } hdr;
+ mlassert_self();
+
+ if (self->is_broken) {
+ lwarning("PCAP is broken, will not read next packet");
+ return 0;
+ }
+
+ if (self->len - self->at < 16) {
+ if (self->at < self->len) {
+ lwarning("could not read next PCAP header, aborting");
+ self->is_broken = 1;
+ }
+ return 0;
+ }
+
+ memcpy(&hdr, &self->buf[self->at], 16);
+ self->at += 16;
+ if (self->is_swapped) {
+ hdr.ts_sec = bswap_32(hdr.ts_sec);
+ hdr.ts_usec = bswap_32(hdr.ts_usec);
+ hdr.incl_len = bswap_32(hdr.incl_len);
+ hdr.orig_len = bswap_32(hdr.orig_len);
+ }
+ if (hdr.incl_len > self->snaplen) {
+ lwarning("invalid packet length, larger then snaplen");
+ self->is_broken = 1;
+ return 0;
+ }
+ if (self->len - self->at < hdr.incl_len) {
+ lwarning("could not read all of packet, aborting");
+ self->is_broken = 1;
+ return 0;
+ }
+
+ self->pkts++;
+
+ self->prod_pkt.ts.sec = hdr.ts_sec;
+ if (self->is_nanosec) {
+ self->prod_pkt.ts.nsec = hdr.ts_usec;
+ } else {
+ self->prod_pkt.ts.nsec = hdr.ts_usec * 1000;
+ }
+ self->prod_pkt.bytes = (unsigned char*)&self->buf[self->at];
+ self->prod_pkt.caplen = hdr.incl_len;
+ self->prod_pkt.len = hdr.orig_len;
+
+ self->at += hdr.incl_len;
+ return (core_object_t*)&self->prod_pkt;
+}
+
+core_producer_t input_mmpcap_producer(input_mmpcap_t* self)
+{
+ mlassert_self();
+
+ if (self->buf == MAP_FAILED) {
+ lfatal("no PCAP opened");
+ }
+
+ return (core_producer_t)_produce;
+}
diff --git a/src/input/mmpcap.h b/src/input/mmpcap.h
new file mode 100644
index 0000000..0873858
--- /dev/null
+++ b/src/input/mmpcap.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "core/log.h"
+#include "core/receiver.h"
+#include "core/producer.h"
+#include "core/object/pcap.h"
+
+#ifndef __dnsjit_input_mmpcap_h
+#define __dnsjit_input_mmpcap_h
+
+#include "input/mmpcap.hh"
+
+#endif
diff --git a/src/input/mmpcap.hh b/src/input/mmpcap.hh
new file mode 100644
index 0000000..27ed35e
--- /dev/null
+++ b/src/input/mmpcap.hh
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//lua:require("dnsjit.core.log")
+//lua:require("dnsjit.core.receiver_h")
+//lua:require("dnsjit.core.producer_h")
+//lua:require("dnsjit.core.object.pcap_h")
+
+typedef struct input_mmpcap {
+ core_log_t _log;
+ core_receiver_t recv;
+ void* ctx;
+
+ uint8_t is_swapped;
+ uint8_t is_nanosec;
+ uint8_t is_broken;
+
+ core_object_pcap_t prod_pkt;
+
+ int fd;
+ size_t len, at;
+ size_t pkts;
+ uint8_t* buf;
+
+ uint32_t magic_number;
+ uint16_t version_major;
+ uint16_t version_minor;
+ int32_t thiszone;
+ uint32_t sigfigs;
+ uint32_t snaplen;
+ uint32_t network;
+
+ uint32_t linktype;
+} input_mmpcap_t;
+
+core_log_t* input_mmpcap_log();
+
+void input_mmpcap_init(input_mmpcap_t* self);
+void input_mmpcap_destroy(input_mmpcap_t* self);
+int input_mmpcap_open(input_mmpcap_t* self, const char* file);
+int input_mmpcap_run(input_mmpcap_t* self);
+
+core_producer_t input_mmpcap_producer(input_mmpcap_t* self);
diff --git a/src/input/mmpcap.lua b/src/input/mmpcap.lua
new file mode 100644
index 0000000..8e28e69
--- /dev/null
+++ b/src/input/mmpcap.lua
@@ -0,0 +1,120 @@
+-- Copyright (c) 2018-2021, OARC, Inc.
+-- All rights reserved.
+--
+-- This file is part of dnsjit.
+--
+-- dnsjit is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- dnsjit is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+
+-- dnsjit.input.mmpcap
+-- Read input from a PCAP file using mmap()
+-- local input = require("dnsjit.input.fpcap").new()
+-- input:open("file.pcap")
+-- input:receiver(filter_or_output)
+-- input:run()
+--
+-- Read input from a PCAP file by mapping the whole file to memory using
+-- .B mmap()
+-- and parse the PCAP without libpcap.
+-- After opening a file and reading the PCAP header, the attributes are
+-- populated.
+-- .SS Attributes
+-- .TP
+-- is_swapped
+-- Indicate if the byte order in the PCAP is in reverse order of the host.
+-- .TP
+-- is_nanosec
+-- Indicate if the time stamps are in nanoseconds or not.
+-- .TP
+-- magic_number
+-- Magic number.
+-- .TP
+-- version_major
+-- Major version number.
+-- .TP
+-- version_minor
+-- Minor version number.
+-- .TP
+-- thiszone
+-- GMT to local correction.
+-- .TP
+-- sigfigs
+-- Accuracy of timestamps.
+-- .TP
+-- snaplen
+-- Max length of captured packets, in octets.
+-- .TP
+-- network
+-- The link type found in the PCAP header, see https://www.tcpdump.org/linktypes.html .
+-- .TP
+-- linktype
+-- The data link type, mapped from
+-- .IR network .
+module(...,package.seeall)
+
+require("dnsjit.input.mmpcap_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "input_mmpcap_t"
+local input_mmpcap_t = ffi.typeof(t_name)
+local Mmpcap = {}
+
+-- Create a new Mmpcap input.
+function Mmpcap.new()
+ local self = {
+ _receiver = nil,
+ obj = input_mmpcap_t(),
+ }
+ C.input_mmpcap_init(self.obj)
+ ffi.gc(self.obj, C.input_mmpcap_destroy)
+ return setmetatable(self, { __index = Mmpcap })
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Mmpcap:log()
+ if self == nil then
+ return C.input_mmpcap_log()
+ end
+ return self.obj._log
+end
+
+-- Set the receiver to pass objects to.
+function Mmpcap:receiver(o)
+ self.obj.recv, self.obj.ctx = o:receive()
+ self._receiver = o
+end
+
+-- Return the C functions and context for producing objects.
+function Mmpcap:produce()
+ return C.input_mmpcap_producer(self.obj), self.obj
+end
+
+-- Open a PCAP file for processing and read the PCAP header.
+-- Returns 0 on success.
+function Mmpcap:open(file)
+ return C.input_mmpcap_open(self.obj, file)
+end
+
+-- Start processing packets and send each packet read to the receiver.
+-- Returns 0 if all packets was read successfully.
+function Mmpcap:run()
+ return C.input_mmpcap_run(self.obj)
+end
+
+-- Return the number of packets seen.
+function Mmpcap:packets()
+ return tonumber(self.obj.pkts)
+end
+
+return Mmpcap
diff --git a/src/input/pcap.c b/src/input/pcap.c
new file mode 100644
index 0000000..25d6ee3
--- /dev/null
+++ b/src/input/pcap.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "input/pcap.h"
+#include "core/assert.h"
+#include "core/object/pcap.h"
+
+static core_log_t _log = LOG_T_INIT("input.pcap");
+static input_pcap_t _defaults = {
+ LOG_T_INIT_OBJ("input.pcap"),
+ 0, 0,
+ 0,
+ CORE_OBJECT_PCAP_INIT(0),
+ 0, 0,
+ 0, 0
+};
+
+core_log_t* input_pcap_log()
+{
+ return &_log;
+}
+
+void input_pcap_init(input_pcap_t* self)
+{
+ mlassert_self();
+
+ *self = _defaults;
+}
+
+void input_pcap_destroy(input_pcap_t* self)
+{
+ mlassert_self();
+
+ if (self->pcap) {
+ pcap_close(self->pcap);
+ }
+}
+
+int input_pcap_create(input_pcap_t* self, const char* source)
+{
+ char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
+ mlassert_self();
+ lassert(source, "source is nil");
+
+ if (self->pcap) {
+ lfatal("already opened");
+ }
+
+ if (!(self->pcap = pcap_create(source, errbuf))) {
+ lcritical("pcap_create(%s) error: %s", source, errbuf);
+ return -1;
+ }
+
+ return 0;
+}
+
+int input_pcap_activate(input_pcap_t* self)
+{
+ int ret;
+
+ mlassert_self();
+ if (!self->pcap) {
+ lfatal("no PCAP opened");
+ }
+
+ if (!(ret = pcap_activate(self->pcap))) {
+ self->snaplen = pcap_snapshot(self->pcap);
+ self->linktype = pcap_datalink(self->pcap);
+ self->is_swapped = 0;
+
+ self->prod_pkt.snaplen = self->snaplen;
+ self->prod_pkt.linktype = self->linktype;
+ self->prod_pkt.is_swapped = self->is_swapped;
+
+ ldebug("pcap v%u.%u snaplen:%lu %s", pcap_major_version(self->pcap), pcap_minor_version(self->pcap), self->snaplen, self->is_swapped ? " swapped" : "");
+ }
+
+ return ret;
+}
+
+int input_pcap_open_offline(input_pcap_t* self, const char* file)
+{
+ char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
+ mlassert_self();
+ lassert(file, "file is nil");
+
+ if (self->pcap) {
+ lfatal("already opened");
+ }
+
+ if (!(self->pcap = pcap_open_offline(file, errbuf))) {
+ lcritical("pcap_open_offline(%s) error: %s", file, errbuf);
+ return -1;
+ }
+
+ self->snaplen = pcap_snapshot(self->pcap);
+ self->linktype = pcap_datalink(self->pcap);
+ self->is_swapped = pcap_is_swapped(self->pcap);
+
+ self->prod_pkt.snaplen = self->snaplen;
+ self->prod_pkt.linktype = self->linktype;
+ self->prod_pkt.is_swapped = self->is_swapped;
+
+ ldebug("pcap v%u.%u snaplen:%lu %s", pcap_major_version(self->pcap), pcap_minor_version(self->pcap), self->snaplen, self->is_swapped ? " swapped" : "");
+
+ return 0;
+}
+
+static void _handler(u_char* user, const struct pcap_pkthdr* h, const u_char* bytes)
+{
+ input_pcap_t* self = (input_pcap_t*)user;
+ core_object_pcap_t pkt = CORE_OBJECT_PCAP_INIT(0);
+ mlassert_self();
+ lassert(h, "pcap_pkthdr is nil");
+ lassert(bytes, "bytes is nil");
+
+ self->pkts++;
+
+ pkt.snaplen = self->snaplen;
+ pkt.linktype = self->linktype;
+ pkt.ts.sec = h->ts.tv_sec;
+ pkt.ts.nsec = h->ts.tv_usec * 1000;
+ pkt.caplen = h->caplen;
+ pkt.len = h->len;
+ pkt.bytes = bytes;
+ pkt.is_swapped = self->is_swapped;
+
+ self->recv(self->ctx, (core_object_t*)&pkt);
+}
+
+int input_pcap_loop(input_pcap_t* self, int cnt)
+{
+ mlassert_self();
+ if (!self->pcap) {
+ lfatal("no PCAP opened");
+ }
+ if (!self->recv) {
+ lfatal("no receiver set");
+ }
+
+ return pcap_loop(self->pcap, cnt, _handler, (void*)self);
+}
+
+int input_pcap_dispatch(input_pcap_t* self, int cnt)
+{
+ mlassert_self();
+ if (!self->pcap) {
+ lfatal("no PCAP opened");
+ }
+ if (!self->recv) {
+ lfatal("no receiver set");
+ }
+
+ return pcap_dispatch(self->pcap, cnt, _handler, (void*)self);
+}
+
+static const core_object_t* _produce(input_pcap_t* self)
+{
+ struct pcap_pkthdr* h;
+ const u_char* bytes;
+ int ret = 0;
+ mlassert_self();
+
+ while (!(ret = pcap_next_ex(self->pcap, &h, &bytes)))
+ ;
+ if (ret == 1 && h && bytes) {
+ self->prod_pkt.ts.sec = h->ts.tv_sec;
+ self->prod_pkt.ts.nsec = h->ts.tv_usec * 1000;
+ self->prod_pkt.caplen = h->caplen;
+ self->prod_pkt.len = h->len;
+ self->prod_pkt.bytes = bytes;
+ return (core_object_t*)&self->prod_pkt;
+ }
+
+ return 0;
+}
+
+core_producer_t input_pcap_producer(input_pcap_t* self)
+{
+ mlassert_self();
+
+ if (!self->pcap) {
+ lfatal("no PCAP opened");
+ }
+
+ return (core_producer_t)_produce;
+}
diff --git a/src/input/pcap.h b/src/input/pcap.h
new file mode 100644
index 0000000..0a53de5
--- /dev/null
+++ b/src/input/pcap.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "core/log.h"
+#include "core/receiver.h"
+#include "core/producer.h"
+#include "core/object/pcap.h"
+
+#ifndef __dnsjit_input_pcap_h
+#define __dnsjit_input_pcap_h
+
+#include <pcap/pcap.h>
+
+#include "input/pcap.hh"
+
+#endif
diff --git a/src/input/pcap.hh b/src/input/pcap.hh
new file mode 100644
index 0000000..a9e0a41
--- /dev/null
+++ b/src/input/pcap.hh
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if 0
+typedef struct pcap {} pcap_t;
+#endif
+
+//lua:require("dnsjit.core.log")
+//lua:require("dnsjit.core.receiver_h")
+//lua:require("dnsjit.core.producer_h")
+//lua:require("dnsjit.core.object.pcap_h")
+
+typedef struct input_pcap {
+ core_log_t _log;
+ core_receiver_t recv;
+ void* ctx;
+
+ uint8_t is_swapped;
+
+ core_object_pcap_t prod_pkt;
+
+ pcap_t* pcap;
+ size_t pkts;
+
+ size_t snaplen;
+ uint32_t linktype;
+} input_pcap_t;
+
+core_log_t* input_pcap_log();
+
+void input_pcap_init(input_pcap_t* self);
+void input_pcap_destroy(input_pcap_t* self);
+int input_pcap_create(input_pcap_t* self, const char* source);
+int input_pcap_activate(input_pcap_t* self);
+int input_pcap_open_offline(input_pcap_t* self, const char* file);
+int input_pcap_loop(input_pcap_t* self, int cnt);
+int input_pcap_dispatch(input_pcap_t* self, int cnt);
+
+core_producer_t input_pcap_producer(input_pcap_t* self);
diff --git a/src/input/pcap.lua b/src/input/pcap.lua
new file mode 100644
index 0000000..89aabd9
--- /dev/null
+++ b/src/input/pcap.lua
@@ -0,0 +1,130 @@
+-- Copyright (c) 2018-2021, OARC, Inc.
+-- All rights reserved.
+--
+-- This file is part of dnsjit.
+--
+-- dnsjit is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- dnsjit is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+
+-- dnsjit.input.pcap
+-- Read input from an interface or PCAP file using libpcap
+-- local input = require("dnsjit.input.pcap").new()
+-- input:open_offline("file.pcap")
+-- input:receiver(filter_or_output)
+-- input:run()
+--
+-- Input module for reading packets from interfaces and PCAP files.
+module(...,package.seeall)
+
+require("dnsjit.input.pcap_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "input_pcap_t"
+local input_pcap_t = ffi.typeof(t_name)
+local Pcap = {}
+
+-- Create a new Pcap input.
+function Pcap.new()
+ local self = {
+ _receiver = nil,
+ obj = input_pcap_t(),
+ }
+ C.input_pcap_init(self.obj)
+ ffi.gc(self.obj, C.input_pcap_destroy)
+ return setmetatable(self, { __index = Pcap })
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Pcap:log()
+ if self == nil then
+ return C.input_pcap_log()
+ end
+ return self.obj._log
+end
+
+-- Set the receiver to pass objects to.
+function Pcap:receiver(o)
+ self.obj.recv, self.obj.ctx = o:receive()
+ self._receiver = o
+end
+
+-- Return the C functions and context for producing objects.
+function Pcap:produce()
+ return C.input_pcap_producer(self.obj), self.obj
+end
+
+-- Open a live packet capture on
+-- .IR source ,
+-- which is an interface name or "any" (Linux) / "all" (BSD).
+-- Must be activated before use.
+function Pcap:create(source)
+ return C.input_pcap_create(self.obj, source)
+end
+
+-- Activate a live packet capture, see
+-- .BR pcap_activate (3pcap)
+-- for more information and possible return values.
+function Pcap:activate()
+ return C.input_pcap_activate(self.obj)
+end
+
+-- Open a PCAP file for processing, see
+-- .BR pcap_open_offline (3pcap)
+-- for more information.
+-- Returns 0 on success.
+function Pcap:open_offline(file)
+ return C.input_pcap_open_offline(self.obj, file)
+end
+
+-- Process packets from a live capture or savefile until
+-- .I cnt
+-- packets are processed, see
+-- .BR pcap_loop (3pcap)
+-- for more information and possible return values.
+function Pcap:loop(cnt)
+ if cnt == nil then
+ cnt = -1
+ end
+ return C.input_pcap_loop(self.obj, cnt)
+end
+
+-- Process packets from a live capture or savefile until
+-- .I cnt
+-- packets are processed, see
+-- .BR pcap_dispatch (3pcap)
+-- for more information and possible return values.
+function Pcap:dispatch(cnt)
+ if cnt == nil then
+ cnt = -1
+ end
+ return C.input_pcap_dispatch(self.obj, cnt)
+end
+
+-- Return the number of packets seen.
+function Pcap:packets()
+ return tonumber(self.obj.pkts)
+end
+
+-- Return the linktype of the opened PCAP.
+function Pcap:linktype()
+ return self.obj.linktype
+end
+
+-- Return the snaplen of the opened PCAP.
+function Pcap:snaplen()
+ return self.obj.snaplen
+end
+
+-- dnsjit.output.pcap (3)
+return Pcap
diff --git a/src/input/zero.c b/src/input/zero.c
new file mode 100644
index 0000000..8bb1cf6
--- /dev/null
+++ b/src/input/zero.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "input/zero.h"
+#include "core/assert.h"
+#include "core/object/null.h"
+
+#include <time.h>
+
+static core_log_t _log = LOG_T_INIT("input.zero");
+static input_zero_t _defaults = {
+ LOG_T_INIT_OBJ("input.zero"),
+ 0,
+ 0,
+};
+
+static core_object_null_t _null = CORE_OBJECT_NULL_INIT(0);
+
+core_log_t* input_zero_log()
+{
+ return &_log;
+}
+
+void input_zero_init(input_zero_t* self)
+{
+ mlassert_self();
+
+ *self = _defaults;
+}
+
+void input_zero_destroy(input_zero_t* self)
+{
+ mlassert_self();
+}
+
+void input_zero_run(input_zero_t* self, uint64_t num)
+{
+ mlassert_self();
+ if (!self->recv) {
+ lfatal("no receiver set");
+ }
+
+ while (num--) {
+ self->recv(self->ctx, (core_object_t*)&_null);
+ }
+}
+
+static const core_object_t* _produce(void* ctx)
+{
+ return (core_object_t*)&_null;
+}
+
+core_producer_t input_zero_producer()
+{
+ return _produce;
+}
diff --git a/src/input/zero.h b/src/input/zero.h
new file mode 100644
index 0000000..882525a
--- /dev/null
+++ b/src/input/zero.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "core/log.h"
+#include "core/receiver.h"
+#include "core/producer.h"
+
+#ifndef __dnsjit_input_zero_h
+#define __dnsjit_input_zero_h
+
+#include <stdint.h>
+
+#include "input/zero.hh"
+
+#endif
diff --git a/src/input/zero.hh b/src/input/zero.hh
new file mode 100644
index 0000000..9373c90
--- /dev/null
+++ b/src/input/zero.hh
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018-2021, OARC, Inc.
+ * All rights reserved.
+ *
+ * This file is part of dnsjit.
+ *
+ * dnsjit is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dnsjit is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//lua:require("dnsjit.core.log")
+//lua:require("dnsjit.core.receiver_h")
+//lua:require("dnsjit.core.producer_h")
+
+typedef struct input_zero {
+ core_log_t _log;
+ core_receiver_t recv;
+ void* ctx;
+} input_zero_t;
+
+core_log_t* input_zero_log();
+
+void input_zero_init(input_zero_t* self);
+void input_zero_destroy(input_zero_t* self);
+void input_zero_run(input_zero_t* self, uint64_t num);
+
+core_producer_t input_zero_producer();
diff --git a/src/input/zero.lua b/src/input/zero.lua
new file mode 100644
index 0000000..1907dce
--- /dev/null
+++ b/src/input/zero.lua
@@ -0,0 +1,75 @@
+-- Copyright (c) 2018-2021, OARC, Inc.
+-- All rights reserved.
+--
+-- This file is part of dnsjit.
+--
+-- dnsjit is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- dnsjit is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+
+-- dnsjit.input.zero
+-- Generate empty objects (/dev/zero)
+-- local input = require("dnsjit.input.zero").new()
+-- input:receiver(filter_or_output)
+-- input:run(1e6)
+--
+-- Input module for generating empty
+-- .I core.object.null
+-- objects, mostly used for testing.
+module(...,package.seeall)
+
+require("dnsjit.input.zero_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "input_zero_t"
+local input_zero_t = ffi.typeof(t_name)
+local Zero = {}
+
+-- Create a new Zero input.
+function Zero.new()
+ local self = {
+ _receiver = nil,
+ obj = input_zero_t(),
+ }
+ C.input_zero_init(self.obj)
+ ffi.gc(self.obj, C.input_zero_destroy)
+ return setmetatable(self, { __index = Zero })
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Zero:log()
+ if self == nil then
+ return C.input_zero_log()
+ end
+ return self.obj._log
+end
+
+-- Set the receiver to pass objects to.
+function Zero:receiver(o)
+ self.obj.recv, self.obj.ctx = o:receive()
+ self._receiver = o
+end
+
+-- Return the C functions and context for producing objects.
+function Zero:produce()
+ return C.input_zero_producer(), self.obj
+end
+
+-- Generate
+-- .I num
+-- empty objects and send them to the receiver.
+function Zero:run(num)
+ C.input_zero_run(self.obj, num)
+end
+
+return Zero