summaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/dnsjit/Makefile.am287
-rw-r--r--include/dnsjit/Makefile.in1652
-rw-r--r--include/dnsjit/config.h.in295
-rw-r--r--include/dnsjit/core.lua45
-rw-r--r--include/dnsjit/core/assert.h46
-rw-r--r--include/dnsjit/core/channel.c169
-rw-r--r--include/dnsjit/core/channel.h41
-rw-r--r--include/dnsjit/core/channel.hh49
-rw-r--r--include/dnsjit/core/channel.lua142
-rw-r--r--include/dnsjit/core/compat.c25
-rw-r--r--include/dnsjit/core/compat.h32
-rw-r--r--include/dnsjit/core/compat.lua41
-rw-r--r--include/dnsjit/core/file.c37
-rw-r--r--include/dnsjit/core/file.h26
-rw-r--r--include/dnsjit/core/file.hh21
-rw-r--r--include/dnsjit/core/file.lua37
-rw-r--r--include/dnsjit/core/loader.lua68
-rw-r--r--include/dnsjit/core/log.c399
-rw-r--r--include/dnsjit/core/log.h105
-rw-r--r--include/dnsjit/core/log.hh44
-rw-r--r--include/dnsjit/core/log.lua639
-rw-r--r--include/dnsjit/core/object.c135
-rw-r--r--include/dnsjit/core/object.h51
-rw-r--r--include/dnsjit/core/object.hh28
-rw-r--r--include/dnsjit/core/object.lua177
-rw-r--r--include/dnsjit/core/object/dns.c471
-rw-r--r--include/dnsjit/core/object/dns.h184
-rw-r--r--include/dnsjit/core/object/dns.hh119
-rw-r--r--include/dnsjit/core/object/dns.lua797
-rw-r--r--include/dnsjit/core/object/dns/label.lua118
-rw-r--r--include/dnsjit/core/object/dns/q.lua52
-rw-r--r--include/dnsjit/core/object/dns/rr.lua85
-rw-r--r--include/dnsjit/core/object/ether.c45
-rw-r--r--include/dnsjit/core/object/ether.h38
-rw-r--r--include/dnsjit/core/object/ether.hh33
-rw-r--r--include/dnsjit/core/object/ether.lua78
-rw-r--r--include/dnsjit/core/object/gre.c45
-rw-r--r--include/dnsjit/core/object/gre.h38
-rw-r--r--include/dnsjit/core/object/gre.hh37
-rw-r--r--include/dnsjit/core/object/gre.lua87
-rw-r--r--include/dnsjit/core/object/icmp.c45
-rw-r--r--include/dnsjit/core/object/icmp.h38
-rw-r--r--include/dnsjit/core/object/icmp.hh33
-rw-r--r--include/dnsjit/core/object/icmp.lua78
-rw-r--r--include/dnsjit/core/object/icmp6.c45
-rw-r--r--include/dnsjit/core/object/icmp6.h38
-rw-r--r--include/dnsjit/core/object/icmp6.hh33
-rw-r--r--include/dnsjit/core/object/icmp6.lua78
-rw-r--r--include/dnsjit/core/object/ieee802.c45
-rw-r--r--include/dnsjit/core/object/ieee802.h38
-rw-r--r--include/dnsjit/core/object/ieee802.hh35
-rw-r--r--include/dnsjit/core/object/ieee802.lua84
-rw-r--r--include/dnsjit/core/object/ip.c45
-rw-r--r--include/dnsjit/core/object/ip.h38
-rw-r--r--include/dnsjit/core/object/ip.hh40
-rw-r--r--include/dnsjit/core/object/ip.lua122
-rw-r--r--include/dnsjit/core/object/ip6.c45
-rw-r--r--include/dnsjit/core/object/ip6.h42
-rw-r--r--include/dnsjit/core/object/ip6.hh42
-rw-r--r--include/dnsjit/core/object/ip6.lua130
-rw-r--r--include/dnsjit/core/object/linuxsll.c45
-rw-r--r--include/dnsjit/core/object/linuxsll.h38
-rw-r--r--include/dnsjit/core/object/linuxsll.hh35
-rw-r--r--include/dnsjit/core/object/linuxsll.lua84
-rw-r--r--include/dnsjit/core/object/loop.c45
-rw-r--r--include/dnsjit/core/object/loop.h38
-rw-r--r--include/dnsjit/core/object/loop.hh31
-rw-r--r--include/dnsjit/core/object/loop.lua72
-rw-r--r--include/dnsjit/core/object/null.c45
-rw-r--r--include/dnsjit/core/object/null.h38
-rw-r--r--include/dnsjit/core/object/null.hh31
-rw-r--r--include/dnsjit/core/object/null.lua72
-rw-r--r--include/dnsjit/core/object/payload.c50
-rw-r--r--include/dnsjit/core/object/payload.h38
-rw-r--r--include/dnsjit/core/object/payload.hh32
-rw-r--r--include/dnsjit/core/object/payload.lua83
-rw-r--r--include/dnsjit/core/object/pcap.c50
-rw-r--r--include/dnsjit/core/object/pcap.h38
-rw-r--r--include/dnsjit/core/object/pcap.hh38
-rw-r--r--include/dnsjit/core/object/pcap.lua98
-rw-r--r--include/dnsjit/core/object/tcp.c45
-rw-r--r--include/dnsjit/core/object/tcp.h39
-rw-r--r--include/dnsjit/core/object/tcp.hh43
-rw-r--r--include/dnsjit/core/object/tcp.lua110
-rw-r--r--include/dnsjit/core/object/udp.c45
-rw-r--r--include/dnsjit/core/object/udp.h38
-rw-r--r--include/dnsjit/core/object/udp.hh34
-rw-r--r--include/dnsjit/core/object/udp.lua86
-rw-r--r--include/dnsjit/core/objects.lua61
-rw-r--r--include/dnsjit/core/producer.c23
-rw-r--r--include/dnsjit/core/producer.h28
-rw-r--r--include/dnsjit/core/producer.hh23
-rw-r--r--include/dnsjit/core/producer.lua28
-rw-r--r--include/dnsjit/core/receiver.c23
-rw-r--r--include/dnsjit/core/receiver.h28
-rw-r--r--include/dnsjit/core/receiver.hh23
-rw-r--r--include/dnsjit/core/receiver.lua28
-rw-r--r--include/dnsjit/core/thread.c229
-rw-r--r--include/dnsjit/core/thread.h31
-rw-r--r--include/dnsjit/core/thread.hh56
-rw-r--r--include/dnsjit/core/thread.lua141
-rw-r--r--include/dnsjit/core/timespec.h33
-rw-r--r--include/dnsjit/core/timespec.hh24
-rw-r--r--include/dnsjit/core/timespec.lua54
-rw-r--r--include/dnsjit/dnsjit.1in144
-rw-r--r--include/dnsjit/dnsjit.c111
-rw-r--r--include/dnsjit/filter.lua31
-rw-r--r--include/dnsjit/filter/copy.c192
-rw-r--r--include/dnsjit/filter/copy.h30
-rw-r--r--include/dnsjit/filter/copy.hh40
-rw-r--r--include/dnsjit/filter/copy.lua74
-rw-r--r--include/dnsjit/filter/ipsplit.c270
-rw-r--r--include/dnsjit/filter/ipsplit.h29
-rw-r--r--include/dnsjit/filter/ipsplit.hh61
-rw-r--r--include/dnsjit/filter/ipsplit.lua122
-rw-r--r--include/dnsjit/filter/layer.c689
-rw-r--r--include/dnsjit/filter/layer.h44
-rw-r--r--include/dnsjit/filter/layer.hh70
-rw-r--r--include/dnsjit/filter/layer.lua93
-rw-r--r--include/dnsjit/filter/split.c114
-rw-r--r--include/dnsjit/filter/split.h29
-rw-r--r--include/dnsjit/filter/split.hh50
-rw-r--r--include/dnsjit/filter/split.lua80
-rw-r--r--include/dnsjit/filter/timing.c557
-rw-r--r--include/dnsjit/filter/timing.h30
-rw-r--r--include/dnsjit/filter/timing.hh52
-rw-r--r--include/dnsjit/filter/timing.lua123
-rw-r--r--include/dnsjit/gen-compat.lua34
-rwxr-xr-xinclude/dnsjit/gen-errno.sh28
-rw-r--r--include/dnsjit/gen-manpage.lua126
-rw-r--r--include/dnsjit/globals.c60
-rw-r--r--include/dnsjit/globals.h28
-rw-r--r--include/dnsjit/input.lua30
-rw-r--r--include/dnsjit/input/fpcap.c349
-rw-r--r--include/dnsjit/input/fpcap.h31
-rw-r--r--include/dnsjit/input/fpcap.hh62
-rw-r--r--include/dnsjit/input/fpcap.lua140
-rw-r--r--include/dnsjit/input/mmpcap.c335
-rw-r--r--include/dnsjit/input/mmpcap.h31
-rw-r--r--include/dnsjit/input/mmpcap.hh60
-rw-r--r--include/dnsjit/input/mmpcap.lua120
-rw-r--r--include/dnsjit/input/pcap.c206
-rw-r--r--include/dnsjit/input/pcap.h33
-rw-r--r--include/dnsjit/input/pcap.hh56
-rw-r--r--include/dnsjit/input/pcap.lua130
-rw-r--r--include/dnsjit/input/zero.lua31
-rw-r--r--include/dnsjit/input/zpcap.c582
-rw-r--r--include/dnsjit/input/zpcap.h31
-rw-r--r--include/dnsjit/input/zpcap.hh77
-rw-r--r--include/dnsjit/input/zpcap.lua159
-rw-r--r--include/dnsjit/lib.lua29
-rw-r--r--include/dnsjit/lib/base64url.c480
-rw-r--r--include/dnsjit/lib/base64url.h28
-rw-r--r--include/dnsjit/lib/base64url.hh99
-rw-r--r--include/dnsjit/lib/base64url.lua97
-rw-r--r--include/dnsjit/lib/clock.c75
-rw-r--r--include/dnsjit/lib/clock.h28
-rw-r--r--include/dnsjit/lib/clock.hh29
-rw-r--r--include/dnsjit/lib/clock.lua47
-rw-r--r--include/dnsjit/lib/getopt.lua368
-rw-r--r--include/dnsjit/lib/ip.lua125
-rw-r--r--include/dnsjit/lib/parseconf.lua181
-rw-r--r--include/dnsjit/lib/trie.c923
-rw-r--r--include/dnsjit/lib/trie.h39
-rw-r--r--include/dnsjit/lib/trie.hh118
-rw-r--r--include/dnsjit/lib/trie.lua172
-rw-r--r--include/dnsjit/lib/trie/iter.lua93
-rw-r--r--include/dnsjit/lib/trie/node.lua84
-rw-r--r--include/dnsjit/output.lua33
-rw-r--r--include/dnsjit/output/dnscli.c888
-rw-r--r--include/dnsjit/output/dnscli.h38
-rw-r--r--include/dnsjit/output/dnscli.hh72
-rw-r--r--include/dnsjit/output/dnscli.lua187
-rw-r--r--include/dnsjit/output/null.lua31
-rw-r--r--include/dnsjit/output/pcap.c120
-rw-r--r--include/dnsjit/output/pcap.h32
-rw-r--r--include/dnsjit/output/pcap.hh42
-rw-r--r--include/dnsjit/output/pcap.lua93
-rw-r--r--include/dnsjit/output/respdiff.c298
-rw-r--r--include/dnsjit/output/respdiff.h31
-rw-r--r--include/dnsjit/output/respdiff.hh36
-rw-r--r--include/dnsjit/output/respdiff.lua94
-rw-r--r--include/dnsjit/output/tcpcli.c381
-rw-r--r--include/dnsjit/output/tcpcli.h32
-rw-r--r--include/dnsjit/output/tcpcli.hh51
-rw-r--r--include/dnsjit/output/tcpcli.lua131
-rw-r--r--include/dnsjit/output/tlscli.c345
-rw-r--r--include/dnsjit/output/tlscli.h34
-rw-r--r--include/dnsjit/output/tlscli.hh52
-rw-r--r--include/dnsjit/output/tlscli.lua103
-rw-r--r--include/dnsjit/output/udpcli.c300
-rw-r--r--include/dnsjit/output/udpcli.h35
-rw-r--r--include/dnsjit/output/udpcli.hh53
-rw-r--r--include/dnsjit/output/udpcli.lua121
-rw-r--r--include/dnsjit/test/Makefile.am52
-rw-r--r--include/dnsjit/test/Makefile.in947
-rw-r--r--include/dnsjit/test/dns.pcapbin0 -> 20228 bytes
-rw-r--r--include/dnsjit/test/dns.pcap.lz4bin0 -> 7560 bytes
-rw-r--r--include/dnsjit/test/dns.pcap.zstbin0 -> 3972 bytes
-rw-r--r--include/dnsjit/test/pellets.pcapbin0 -> 9177 bytes
-rwxr-xr-xinclude/dnsjit/test/test-base64url.sh20
-rwxr-xr-xinclude/dnsjit/test/test-ipsplit.sh20
-rwxr-xr-xinclude/dnsjit/test/test-trie.sh20
-rw-r--r--include/dnsjit/test/test1.gold1493
-rwxr-xr-xinclude/dnsjit/test/test1.sh31
-rw-r--r--include/dnsjit/test/test2.gold42
-rwxr-xr-xinclude/dnsjit/test/test2.sh21
-rw-r--r--include/dnsjit/test/test3.gold82
-rwxr-xr-xinclude/dnsjit/test/test3.sh22
-rw-r--r--include/dnsjit/test/test4.gold82
-rwxr-xr-xinclude/dnsjit/test/test4.sh21
-rwxr-xr-xinclude/dnsjit/test/test6.sh23
-rw-r--r--include/dnsjit/test/test_base64url.lua24
-rw-r--r--include/dnsjit/test/test_compressupport.lua10
-rwxr-xr-xinclude/dnsjit/test/test_ipsplit.lua294
-rwxr-xr-xinclude/dnsjit/test/test_trie.lua134
-rw-r--r--include/dnsjit/version.h28
217 files changed, 25974 insertions, 0 deletions
diff --git a/include/dnsjit/Makefile.am b/include/dnsjit/Makefile.am
new file mode 100644
index 0000000..3090342
--- /dev/null
+++ b/include/dnsjit/Makefile.am
@@ -0,0 +1,287 @@
+# 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/>.
+
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+CLEANFILES = *.gcda *.gcno *.gcov
+
+SUBDIRS = test
+
+AM_CFLAGS = -Werror=attributes \
+ -I$(srcdir) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/include \
+ $(SIMD_FLAGS) $(CPUEXT_FLAGS) \
+ $(PTHREAD_CFLAGS) \
+ $(luajit_CFLAGS) \
+ $(liblz4_CFLAGS) $(libzstd_CFLAGS)
+
+EXTRA_DIST = gen-manpage.lua gen-compat.lua gen-errno.sh dnsjit.1in
+
+BUILT_SOURCES = core/compat.hh core/log_errstr.c
+
+bin_PROGRAMS = dnsjit
+
+dnsjit_SOURCES = dnsjit.c globals.c
+dist_dnsjit_SOURCES = core.lua lib.lua input.lua filter.lua output.lua
+dnsjitincludedir = $(includedir)/dnsjit
+nobase_dnsjitinclude_HEADERS = globals.h version.h
+lua_hobjects = core/compat.luaho
+lua_objects = core.luao lib.luao input.luao filter.luao output.luao
+dnsjit_LDADD = $(PTHREAD_LIBS) $(luajit_LIBS) $(liblz4_LIBS) $(libzstd_LIBS)
+
+# C source and headers
+dnsjit_SOURCES += core/channel.c core/compat.c core/file.c core/log.c core/object.c core/object/dns.c core/object/ether.c core/object/gre.c core/object/icmp6.c core/object/icmp.c core/object/ieee802.c core/object/ip6.c core/object/ip.c core/object/linuxsll.c core/object/loop.c core/object/null.c core/object/payload.c core/object/pcap.c core/object/tcp.c core/object/udp.c core/producer.c core/receiver.c core/thread.c filter/copy.c filter/ipsplit.c filter/layer.c filter/split.c filter/timing.c input/fpcap.c input/mmpcap.c input/pcap.c input/zpcap.c lib/base64url.c lib/clock.c lib/trie.c output/dnscli.c output/pcap.c output/respdiff.c output/tcpcli.c output/tlscli.c output/udpcli.c
+nobase_dnsjitinclude_HEADERS += core/assert.h core/channel.h core/compat.h core/file.h core/log.h core/object/dns.h core/object/ether.h core/object/gre.h core/object.h core/object/icmp6.h core/object/icmp.h core/object/ieee802.h core/object/ip6.h core/object/ip.h core/object/linuxsll.h core/object/loop.h core/object/null.h core/object/payload.h core/object/pcap.h core/object/tcp.h core/object/udp.h core/producer.h core/receiver.h core/thread.h core/timespec.h filter/copy.h filter/ipsplit.h filter/layer.h filter/split.h filter/timing.h input/fpcap.h input/mmpcap.h input/pcap.h input/zpcap.h lib/base64url.h lib/clock.h lib/trie.h output/dnscli.h output/pcap.h output/respdiff.h output/tcpcli.h output/tlscli.h output/udpcli.h
+
+# Lua headers
+nobase_dnsjitinclude_HEADERS += core/channel.hh core/file.hh core/log.hh core/object/dns.hh core/object/ether.hh core/object/gre.hh core/object.hh core/object/icmp6.hh core/object/icmp.hh core/object/ieee802.hh core/object/ip6.hh core/object/ip.hh core/object/linuxsll.hh core/object/loop.hh core/object/null.hh core/object/payload.hh core/object/pcap.hh core/object/tcp.hh core/object/udp.hh core/producer.hh core/receiver.hh core/thread.hh core/timespec.hh filter/copy.hh filter/ipsplit.hh filter/layer.hh filter/split.hh filter/timing.hh input/fpcap.hh input/mmpcap.hh input/pcap.hh input/zpcap.hh lib/base64url.hh lib/clock.hh lib/trie.hh output/dnscli.hh output/pcap.hh output/respdiff.hh output/tcpcli.hh output/tlscli.hh output/udpcli.hh
+lua_hobjects += core/channel.luaho core/file.luaho core/log.luaho core/object/dns.luaho core/object/ether.luaho core/object/gre.luaho core/object/icmp6.luaho core/object/icmp.luaho core/object/ieee802.luaho core/object/ip6.luaho core/object/ip.luaho core/object/linuxsll.luaho core/object/loop.luaho core/object.luaho core/object/null.luaho core/object/payload.luaho core/object/pcap.luaho core/object/tcp.luaho core/object/udp.luaho core/producer.luaho core/receiver.luaho core/thread.luaho core/timespec.luaho filter/copy.luaho filter/ipsplit.luaho filter/layer.luaho filter/split.luaho filter/timing.luaho input/fpcap.luaho input/mmpcap.luaho input/pcap.luaho input/zpcap.luaho lib/base64url.luaho lib/clock.luaho lib/trie.luaho output/dnscli.luaho output/pcap.luaho output/respdiff.luaho output/tcpcli.luaho output/tlscli.luaho output/udpcli.luaho
+
+# Lua sources
+dist_dnsjit_SOURCES += core/channel.lua core/compat.lua core/file.lua core/loader.lua core/log.lua core/object/dns/label.lua core/object/dns.lua core/object/dns/q.lua core/object/dns/rr.lua core/object/ether.lua core/object/gre.lua core/object/icmp6.lua core/object/icmp.lua core/object/ieee802.lua core/object/ip6.lua core/object/ip.lua core/object/linuxsll.lua core/object/loop.lua core/object.lua core/object/null.lua core/object/payload.lua core/object/pcap.lua core/objects.lua core/object/tcp.lua core/object/udp.lua core/producer.lua core/receiver.lua core/thread.lua core/timespec.lua filter/copy.lua filter/ipsplit.lua filter/layer.lua filter/split.lua filter/timing.lua input/fpcap.lua input/mmpcap.lua input/pcap.lua input/zero.lua input/zpcap.lua lib/base64url.lua lib/clock.lua lib/getopt.lua lib/ip.lua lib/parseconf.lua lib/trie/iter.lua lib/trie.lua lib/trie/node.lua output/dnscli.lua output/null.lua output/pcap.lua output/respdiff.lua output/tcpcli.lua output/tlscli.lua output/udpcli.lua
+lua_objects += core/channel.luao core/compat.luao core/file.luao core/loader.luao core/log.luao core/object/dns/label.luao core/object/dns.luao core/object/dns/q.luao core/object/dns/rr.luao core/object/ether.luao core/object/gre.luao core/object/icmp6.luao core/object/icmp.luao core/object/ieee802.luao core/object/ip6.luao core/object/ip.luao core/object/linuxsll.luao core/object/loop.luao core/object.luao core/object/null.luao core/object/payload.luao core/object/pcap.luao core/objects.luao core/object/tcp.luao core/object/udp.luao core/producer.luao core/receiver.luao core/thread.luao core/timespec.luao filter/copy.luao filter/ipsplit.luao filter/layer.luao filter/split.luao filter/timing.luao input/fpcap.luao input/mmpcap.luao input/pcap.luao input/zero.luao input/zpcap.luao lib/base64url.luao lib/clock.luao lib/getopt.luao lib/ip.luao lib/parseconf.luao lib/trie/iter.luao lib/trie.luao lib/trie/node.luao output/dnscli.luao output/null.luao output/pcap.luao output/respdiff.luao output/tcpcli.luao output/tlscli.luao output/udpcli.luao
+
+dnsjit_LDFLAGS = -Wl,-E
+dnsjit_LDADD += $(lua_hobjects) $(lua_objects)
+CLEANFILES += $(lua_hobjects) $(lua_objects)
+
+man1_MANS = dnsjit.1
+CLEANFILES += $(man1_MANS)
+
+man3_MANS = dnsjit.core.3 dnsjit.lib.3 dnsjit.input.3 dnsjit.filter.3 dnsjit.output.3
+man3_MANS += dnsjit.core.channel.3 dnsjit.core.compat.3 dnsjit.core.file.3 dnsjit.core.loader.3 dnsjit.core.log.3 dnsjit.core.object.3 dnsjit.core.object.dns.3 dnsjit.core.object.dns.label.3 dnsjit.core.object.dns.q.3 dnsjit.core.object.dns.rr.3 dnsjit.core.object.ether.3 dnsjit.core.object.gre.3 dnsjit.core.object.icmp.3 dnsjit.core.object.icmp6.3 dnsjit.core.object.ieee802.3 dnsjit.core.object.ip.3 dnsjit.core.object.ip6.3 dnsjit.core.object.linuxsll.3 dnsjit.core.object.loop.3 dnsjit.core.object.null.3 dnsjit.core.object.payload.3 dnsjit.core.object.pcap.3 dnsjit.core.objects.3 dnsjit.core.object.tcp.3 dnsjit.core.object.udp.3 dnsjit.core.producer.3 dnsjit.core.receiver.3 dnsjit.core.thread.3 dnsjit.core.timespec.3 dnsjit.filter.copy.3 dnsjit.filter.ipsplit.3 dnsjit.filter.layer.3 dnsjit.filter.split.3 dnsjit.filter.timing.3 dnsjit.input.fpcap.3 dnsjit.input.mmpcap.3 dnsjit.input.pcap.3 dnsjit.input.zero.3 dnsjit.input.zpcap.3 dnsjit.lib.base64url.3 dnsjit.lib.clock.3 dnsjit.lib.getopt.3 dnsjit.lib.ip.3 dnsjit.lib.parseconf.3 dnsjit.lib.trie.3 dnsjit.lib.trie.iter.3 dnsjit.lib.trie.node.3 dnsjit.output.dnscli.3 dnsjit.output.null.3 dnsjit.output.pcap.3 dnsjit.output.respdiff.3 dnsjit.output.tcpcli.3 dnsjit.output.tlscli.3 dnsjit.output.udpcli.3
+CLEANFILES += *.3in $(man3_MANS)
+
+.lua.luao:
+ @mkdir -p `dirname "$@"`
+ $(LUAJIT) -bg -n "dnsjit.`echo \"$@\" | sed 's%\..*%%' | sed 's%/%.%g'`" -t o "$<" "$@"
+
+.luah.luaho:
+ @mkdir -p `dirname "$@"`
+ $(LUAJIT) -bg -n "dnsjit.`echo \"$@\" | sed 's%\..*%%' | sed 's%/%.%g'`_h" -t o "$<" "$@"
+
+.hh.luah:
+ @mkdir -p `dirname "$@"`
+ @echo 'module(...,package.seeall);' > "$@"
+ @cat "$<" | grep '^//lua:' | sed 's%^//lua:%%' >> "$@"
+ @echo 'require("ffi").cdef[[' >> "$@"
+ @cat "$<" | grep -v '^#' >> "$@"
+ @echo ']]' >> "$@"
+
+.1in.1:
+ sed -e 's,[@]PACKAGE_VERSION[@],$(PACKAGE_VERSION),g' \
+ -e 's,[@]PACKAGE_URL[@],$(PACKAGE_URL),g' \
+ -e 's,[@]PACKAGE_BUGREPORT[@],$(PACKAGE_BUGREPORT),g' \
+ < "$<" > "$@"
+
+.3in.3:
+ sed -e 's,[@]PACKAGE_VERSION[@],$(PACKAGE_VERSION),g' \
+ -e 's,[@]PACKAGE_URL[@],$(PACKAGE_URL),g' \
+ -e 's,[@]PACKAGE_BUGREPORT[@],$(PACKAGE_BUGREPORT),g' \
+ < "$<" > "$@"
+
+if ENABLE_GCOV
+gcov-local:
+ for src in $(dnsjit_SOURCES); do \
+ gcov -x -l -r -s "$(srcdir)" "$$src"; \
+ done
+endif
+
+core/compat.hh: gen-compat.lua
+ $(LUAJIT) "$(srcdir)/gen-compat.lua" > "$@"
+
+core/log_errstr.c: gen-errno.sh
+ "$(srcdir)/gen-errno.sh" > "$@"
+
+
+dnsjit.core.3in: core.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core.lua" > "$@"
+
+dnsjit.lib.3in: lib.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib.lua" > "$@"
+
+dnsjit.input.3in: input.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/input.lua" > "$@"
+
+dnsjit.filter.3in: filter.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/filter.lua" > "$@"
+
+dnsjit.output.3in: output.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output.lua" > "$@"
+
+dnsjit.core.channel.3in: core/channel.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/channel.lua" > "$@"
+
+dnsjit.core.compat.3in: core/compat.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/compat.lua" > "$@"
+
+dnsjit.core.file.3in: core/file.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/file.lua" > "$@"
+
+dnsjit.core.loader.3in: core/loader.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/loader.lua" > "$@"
+
+dnsjit.core.log.3in: core/log.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/log.lua" > "$@"
+
+dnsjit.core.object.dns.label.3in: core/object/dns/label.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/dns/label.lua" > "$@"
+
+dnsjit.core.object.dns.3in: core/object/dns.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/dns.lua" > "$@"
+
+dnsjit.core.object.dns.q.3in: core/object/dns/q.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/dns/q.lua" > "$@"
+
+dnsjit.core.object.dns.rr.3in: core/object/dns/rr.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/dns/rr.lua" > "$@"
+
+dnsjit.core.object.ether.3in: core/object/ether.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/ether.lua" > "$@"
+
+dnsjit.core.object.gre.3in: core/object/gre.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/gre.lua" > "$@"
+
+dnsjit.core.object.icmp6.3in: core/object/icmp6.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/icmp6.lua" > "$@"
+
+dnsjit.core.object.icmp.3in: core/object/icmp.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/icmp.lua" > "$@"
+
+dnsjit.core.object.ieee802.3in: core/object/ieee802.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/ieee802.lua" > "$@"
+
+dnsjit.core.object.ip6.3in: core/object/ip6.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/ip6.lua" > "$@"
+
+dnsjit.core.object.ip.3in: core/object/ip.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/ip.lua" > "$@"
+
+dnsjit.core.object.linuxsll.3in: core/object/linuxsll.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/linuxsll.lua" > "$@"
+
+dnsjit.core.object.loop.3in: core/object/loop.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/loop.lua" > "$@"
+
+dnsjit.core.object.3in: core/object.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object.lua" > "$@"
+
+dnsjit.core.object.null.3in: core/object/null.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/null.lua" > "$@"
+
+dnsjit.core.object.payload.3in: core/object/payload.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/payload.lua" > "$@"
+
+dnsjit.core.object.pcap.3in: core/object/pcap.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/pcap.lua" > "$@"
+
+dnsjit.core.objects.3in: core/objects.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/objects.lua" > "$@"
+
+dnsjit.core.object.tcp.3in: core/object/tcp.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/tcp.lua" > "$@"
+
+dnsjit.core.object.udp.3in: core/object/udp.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/udp.lua" > "$@"
+
+dnsjit.core.producer.3in: core/producer.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/producer.lua" > "$@"
+
+dnsjit.core.receiver.3in: core/receiver.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/receiver.lua" > "$@"
+
+dnsjit.core.thread.3in: core/thread.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/thread.lua" > "$@"
+
+dnsjit.core.timespec.3in: core/timespec.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/timespec.lua" > "$@"
+
+dnsjit.filter.copy.3in: filter/copy.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/filter/copy.lua" > "$@"
+
+dnsjit.filter.ipsplit.3in: filter/ipsplit.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/filter/ipsplit.lua" > "$@"
+
+dnsjit.filter.layer.3in: filter/layer.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/filter/layer.lua" > "$@"
+
+dnsjit.filter.split.3in: filter/split.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/filter/split.lua" > "$@"
+
+dnsjit.filter.timing.3in: filter/timing.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/filter/timing.lua" > "$@"
+
+dnsjit.input.fpcap.3in: input/fpcap.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/input/fpcap.lua" > "$@"
+
+dnsjit.input.mmpcap.3in: input/mmpcap.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/input/mmpcap.lua" > "$@"
+
+dnsjit.input.pcap.3in: input/pcap.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/input/pcap.lua" > "$@"
+
+dnsjit.input.zero.3in: input/zero.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/input/zero.lua" > "$@"
+
+dnsjit.input.zpcap.3in: input/zpcap.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/input/zpcap.lua" > "$@"
+
+dnsjit.lib.base64url.3in: lib/base64url.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/base64url.lua" > "$@"
+
+dnsjit.lib.clock.3in: lib/clock.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/clock.lua" > "$@"
+
+dnsjit.lib.getopt.3in: lib/getopt.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/getopt.lua" > "$@"
+
+dnsjit.lib.ip.3in: lib/ip.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/ip.lua" > "$@"
+
+dnsjit.lib.parseconf.3in: lib/parseconf.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/parseconf.lua" > "$@"
+
+dnsjit.lib.trie.iter.3in: lib/trie/iter.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/trie/iter.lua" > "$@"
+
+dnsjit.lib.trie.3in: lib/trie.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/trie.lua" > "$@"
+
+dnsjit.lib.trie.node.3in: lib/trie/node.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/trie/node.lua" > "$@"
+
+dnsjit.output.dnscli.3in: output/dnscli.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/dnscli.lua" > "$@"
+
+dnsjit.output.null.3in: output/null.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/null.lua" > "$@"
+
+dnsjit.output.pcap.3in: output/pcap.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/pcap.lua" > "$@"
+
+dnsjit.output.respdiff.3in: output/respdiff.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/respdiff.lua" > "$@"
+
+dnsjit.output.tcpcli.3in: output/tcpcli.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/tcpcli.lua" > "$@"
+
+dnsjit.output.tlscli.3in: output/tlscli.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/tlscli.lua" > "$@"
+
+dnsjit.output.udpcli.3in: output/udpcli.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/udpcli.lua" > "$@"
diff --git a/include/dnsjit/Makefile.in b/include/dnsjit/Makefile.in
new file mode 100644
index 0000000..7449ba5
--- /dev/null
+++ b/include/dnsjit/Makefile.in
@@ -0,0 +1,1652 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# 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/>.
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = dnsjit$(EXEEXT)
+subdir = src
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cflags_warn_all.m4 \
+ $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_compiler_vendor.m4 \
+ $(top_srcdir)/m4/ax_ext.m4 \
+ $(top_srcdir)/m4/ax_gcc_x86_avx_xgetbv.m4 \
+ $(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \
+ $(top_srcdir)/m4/ax_prepend_flag.m4 \
+ $(top_srcdir)/m4/ax_pthread.m4 \
+ $(top_srcdir)/m4/ax_require_defined.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(nobase_dnsjitinclude_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" \
+ "$(DESTDIR)$(man3dir)" "$(DESTDIR)$(dnsjitincludedir)"
+PROGRAMS = $(bin_PROGRAMS)
+am__dirstamp = $(am__leading_dot)dirstamp
+am_dnsjit_OBJECTS = dnsjit.$(OBJEXT) globals.$(OBJEXT) \
+ core/channel.$(OBJEXT) core/compat.$(OBJEXT) \
+ core/file.$(OBJEXT) core/log.$(OBJEXT) core/object.$(OBJEXT) \
+ core/object/dns.$(OBJEXT) core/object/ether.$(OBJEXT) \
+ core/object/gre.$(OBJEXT) core/object/icmp6.$(OBJEXT) \
+ core/object/icmp.$(OBJEXT) core/object/ieee802.$(OBJEXT) \
+ core/object/ip6.$(OBJEXT) core/object/ip.$(OBJEXT) \
+ core/object/linuxsll.$(OBJEXT) core/object/loop.$(OBJEXT) \
+ core/object/null.$(OBJEXT) core/object/payload.$(OBJEXT) \
+ core/object/pcap.$(OBJEXT) core/object/tcp.$(OBJEXT) \
+ core/object/udp.$(OBJEXT) core/producer.$(OBJEXT) \
+ core/receiver.$(OBJEXT) core/thread.$(OBJEXT) \
+ filter/copy.$(OBJEXT) filter/ipsplit.$(OBJEXT) \
+ filter/layer.$(OBJEXT) filter/split.$(OBJEXT) \
+ filter/timing.$(OBJEXT) input/fpcap.$(OBJEXT) \
+ input/mmpcap.$(OBJEXT) input/pcap.$(OBJEXT) \
+ input/zpcap.$(OBJEXT) lib/base64url.$(OBJEXT) \
+ lib/clock.$(OBJEXT) lib/trie.$(OBJEXT) output/dnscli.$(OBJEXT) \
+ output/pcap.$(OBJEXT) output/respdiff.$(OBJEXT) \
+ output/tcpcli.$(OBJEXT) output/tlscli.$(OBJEXT) \
+ output/udpcli.$(OBJEXT)
+dist_dnsjit_OBJECTS =
+dnsjit_OBJECTS = $(am_dnsjit_OBJECTS) $(dist_dnsjit_OBJECTS)
+am__DEPENDENCIES_1 =
+dnsjit_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(lua_hobjects) \
+ $(lua_objects)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+dnsjit_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(dnsjit_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/dnsjit.Po ./$(DEPDIR)/globals.Po \
+ core/$(DEPDIR)/channel.Po core/$(DEPDIR)/compat.Po \
+ core/$(DEPDIR)/file.Po core/$(DEPDIR)/log.Po \
+ core/$(DEPDIR)/object.Po core/$(DEPDIR)/producer.Po \
+ core/$(DEPDIR)/receiver.Po core/$(DEPDIR)/thread.Po \
+ core/object/$(DEPDIR)/dns.Po core/object/$(DEPDIR)/ether.Po \
+ core/object/$(DEPDIR)/gre.Po core/object/$(DEPDIR)/icmp.Po \
+ core/object/$(DEPDIR)/icmp6.Po \
+ core/object/$(DEPDIR)/ieee802.Po core/object/$(DEPDIR)/ip.Po \
+ core/object/$(DEPDIR)/ip6.Po core/object/$(DEPDIR)/linuxsll.Po \
+ core/object/$(DEPDIR)/loop.Po core/object/$(DEPDIR)/null.Po \
+ core/object/$(DEPDIR)/payload.Po core/object/$(DEPDIR)/pcap.Po \
+ core/object/$(DEPDIR)/tcp.Po core/object/$(DEPDIR)/udp.Po \
+ filter/$(DEPDIR)/copy.Po filter/$(DEPDIR)/ipsplit.Po \
+ filter/$(DEPDIR)/layer.Po filter/$(DEPDIR)/split.Po \
+ filter/$(DEPDIR)/timing.Po input/$(DEPDIR)/fpcap.Po \
+ input/$(DEPDIR)/mmpcap.Po input/$(DEPDIR)/pcap.Po \
+ input/$(DEPDIR)/zpcap.Po lib/$(DEPDIR)/base64url.Po \
+ lib/$(DEPDIR)/clock.Po lib/$(DEPDIR)/trie.Po \
+ output/$(DEPDIR)/dnscli.Po output/$(DEPDIR)/pcap.Po \
+ output/$(DEPDIR)/respdiff.Po output/$(DEPDIR)/tcpcli.Po \
+ output/$(DEPDIR)/tlscli.Po output/$(DEPDIR)/udpcli.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(dnsjit_SOURCES) $(dist_dnsjit_SOURCES)
+DIST_SOURCES = $(dnsjit_SOURCES) $(dist_dnsjit_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man1dir = $(mandir)/man1
+man3dir = $(mandir)/man3
+NROFF = nroff
+MANS = $(man1_MANS) $(man3_MANS)
+HEADERS = $(nobase_dnsjitinclude_HEADERS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__extra_recursive_targets = gcov-recursive
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
+ $(LISP)config.h.in
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPUEXT_FLAGS = @CPUEXT_FLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LUAJIT = @LUAJIT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SIMD_FLAGS = @SIMD_FLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+ax_pthread_config = @ax_pthread_config@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+ck_CFLAGS = @ck_CFLAGS@
+ck_LIBS = @ck_LIBS@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+liblz4_CFLAGS = @liblz4_CFLAGS@
+liblz4_LIBS = @liblz4_LIBS@
+libzstd_CFLAGS = @libzstd_CFLAGS@
+libzstd_LIBS = @libzstd_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+luajit_CFLAGS = @luajit_CFLAGS@
+luajit_LIBS = @luajit_LIBS@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+CLEANFILES = *.gcda *.gcno *.gcov $(lua_hobjects) $(lua_objects) \
+ $(man1_MANS) *.3in $(man3_MANS)
+SUBDIRS = test
+AM_CFLAGS = -Werror=attributes \
+ -I$(srcdir) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/include \
+ $(SIMD_FLAGS) $(CPUEXT_FLAGS) \
+ $(PTHREAD_CFLAGS) \
+ $(luajit_CFLAGS) \
+ $(liblz4_CFLAGS) $(libzstd_CFLAGS)
+
+EXTRA_DIST = gen-manpage.lua gen-compat.lua gen-errno.sh dnsjit.1in
+BUILT_SOURCES = core/compat.hh core/log_errstr.c
+
+# C source and headers
+dnsjit_SOURCES = dnsjit.c globals.c core/channel.c core/compat.c \
+ core/file.c core/log.c core/object.c core/object/dns.c \
+ core/object/ether.c core/object/gre.c core/object/icmp6.c \
+ core/object/icmp.c core/object/ieee802.c core/object/ip6.c \
+ core/object/ip.c core/object/linuxsll.c core/object/loop.c \
+ core/object/null.c core/object/payload.c core/object/pcap.c \
+ core/object/tcp.c core/object/udp.c core/producer.c \
+ core/receiver.c core/thread.c filter/copy.c filter/ipsplit.c \
+ filter/layer.c filter/split.c filter/timing.c input/fpcap.c \
+ input/mmpcap.c input/pcap.c input/zpcap.c lib/base64url.c \
+ lib/clock.c lib/trie.c output/dnscli.c output/pcap.c \
+ output/respdiff.c output/tcpcli.c output/tlscli.c \
+ output/udpcli.c
+
+# Lua sources
+dist_dnsjit_SOURCES = core.lua lib.lua input.lua filter.lua output.lua \
+ core/channel.lua core/compat.lua core/file.lua core/loader.lua \
+ core/log.lua core/object/dns/label.lua core/object/dns.lua \
+ core/object/dns/q.lua core/object/dns/rr.lua \
+ core/object/ether.lua core/object/gre.lua \
+ core/object/icmp6.lua core/object/icmp.lua \
+ core/object/ieee802.lua core/object/ip6.lua core/object/ip.lua \
+ core/object/linuxsll.lua core/object/loop.lua core/object.lua \
+ core/object/null.lua core/object/payload.lua \
+ core/object/pcap.lua core/objects.lua core/object/tcp.lua \
+ core/object/udp.lua core/producer.lua core/receiver.lua \
+ core/thread.lua core/timespec.lua filter/copy.lua \
+ filter/ipsplit.lua filter/layer.lua filter/split.lua \
+ filter/timing.lua input/fpcap.lua input/mmpcap.lua \
+ input/pcap.lua input/zero.lua input/zpcap.lua \
+ lib/base64url.lua lib/clock.lua lib/getopt.lua lib/ip.lua \
+ lib/parseconf.lua lib/trie/iter.lua lib/trie.lua \
+ lib/trie/node.lua output/dnscli.lua output/null.lua \
+ output/pcap.lua output/respdiff.lua output/tcpcli.lua \
+ output/tlscli.lua output/udpcli.lua
+dnsjitincludedir = $(includedir)/dnsjit
+
+# Lua headers
+nobase_dnsjitinclude_HEADERS = globals.h version.h core/assert.h \
+ core/channel.h core/compat.h core/file.h core/log.h \
+ core/object/dns.h core/object/ether.h core/object/gre.h \
+ core/object.h core/object/icmp6.h core/object/icmp.h \
+ core/object/ieee802.h core/object/ip6.h core/object/ip.h \
+ core/object/linuxsll.h core/object/loop.h core/object/null.h \
+ core/object/payload.h core/object/pcap.h core/object/tcp.h \
+ core/object/udp.h core/producer.h core/receiver.h \
+ core/thread.h core/timespec.h filter/copy.h filter/ipsplit.h \
+ filter/layer.h filter/split.h filter/timing.h input/fpcap.h \
+ input/mmpcap.h input/pcap.h input/zpcap.h lib/base64url.h \
+ lib/clock.h lib/trie.h output/dnscli.h output/pcap.h \
+ output/respdiff.h output/tcpcli.h output/tlscli.h \
+ output/udpcli.h core/channel.hh core/file.hh core/log.hh \
+ core/object/dns.hh core/object/ether.hh core/object/gre.hh \
+ core/object.hh core/object/icmp6.hh core/object/icmp.hh \
+ core/object/ieee802.hh core/object/ip6.hh core/object/ip.hh \
+ core/object/linuxsll.hh core/object/loop.hh \
+ core/object/null.hh core/object/payload.hh core/object/pcap.hh \
+ core/object/tcp.hh core/object/udp.hh core/producer.hh \
+ core/receiver.hh core/thread.hh core/timespec.hh \
+ filter/copy.hh filter/ipsplit.hh filter/layer.hh \
+ filter/split.hh filter/timing.hh input/fpcap.hh \
+ input/mmpcap.hh input/pcap.hh input/zpcap.hh lib/base64url.hh \
+ lib/clock.hh lib/trie.hh output/dnscli.hh output/pcap.hh \
+ output/respdiff.hh output/tcpcli.hh output/tlscli.hh \
+ output/udpcli.hh
+lua_hobjects = core/compat.luaho core/channel.luaho core/file.luaho \
+ core/log.luaho core/object/dns.luaho core/object/ether.luaho \
+ core/object/gre.luaho core/object/icmp6.luaho \
+ core/object/icmp.luaho core/object/ieee802.luaho \
+ core/object/ip6.luaho core/object/ip.luaho \
+ core/object/linuxsll.luaho core/object/loop.luaho \
+ core/object.luaho core/object/null.luaho \
+ core/object/payload.luaho core/object/pcap.luaho \
+ core/object/tcp.luaho core/object/udp.luaho \
+ core/producer.luaho core/receiver.luaho core/thread.luaho \
+ core/timespec.luaho filter/copy.luaho filter/ipsplit.luaho \
+ filter/layer.luaho filter/split.luaho filter/timing.luaho \
+ input/fpcap.luaho input/mmpcap.luaho input/pcap.luaho \
+ input/zpcap.luaho lib/base64url.luaho lib/clock.luaho \
+ lib/trie.luaho output/dnscli.luaho output/pcap.luaho \
+ output/respdiff.luaho output/tcpcli.luaho output/tlscli.luaho \
+ output/udpcli.luaho
+lua_objects = core.luao lib.luao input.luao filter.luao output.luao \
+ core/channel.luao core/compat.luao core/file.luao \
+ core/loader.luao core/log.luao core/object/dns/label.luao \
+ core/object/dns.luao core/object/dns/q.luao \
+ core/object/dns/rr.luao core/object/ether.luao \
+ core/object/gre.luao core/object/icmp6.luao \
+ core/object/icmp.luao core/object/ieee802.luao \
+ core/object/ip6.luao core/object/ip.luao \
+ core/object/linuxsll.luao core/object/loop.luao \
+ core/object.luao core/object/null.luao \
+ core/object/payload.luao core/object/pcap.luao \
+ core/objects.luao core/object/tcp.luao core/object/udp.luao \
+ core/producer.luao core/receiver.luao core/thread.luao \
+ core/timespec.luao filter/copy.luao filter/ipsplit.luao \
+ filter/layer.luao filter/split.luao filter/timing.luao \
+ input/fpcap.luao input/mmpcap.luao input/pcap.luao \
+ input/zero.luao input/zpcap.luao lib/base64url.luao \
+ lib/clock.luao lib/getopt.luao lib/ip.luao lib/parseconf.luao \
+ lib/trie/iter.luao lib/trie.luao lib/trie/node.luao \
+ output/dnscli.luao output/null.luao output/pcap.luao \
+ output/respdiff.luao output/tcpcli.luao output/tlscli.luao \
+ output/udpcli.luao
+dnsjit_LDADD = $(PTHREAD_LIBS) $(luajit_LIBS) $(liblz4_LIBS) \
+ $(libzstd_LIBS) $(lua_hobjects) $(lua_objects)
+dnsjit_LDFLAGS = -Wl,-E
+man1_MANS = dnsjit.1
+man3_MANS = dnsjit.core.3 dnsjit.lib.3 dnsjit.input.3 dnsjit.filter.3 \
+ dnsjit.output.3 dnsjit.core.channel.3 dnsjit.core.compat.3 \
+ dnsjit.core.file.3 dnsjit.core.loader.3 dnsjit.core.log.3 \
+ dnsjit.core.object.3 dnsjit.core.object.dns.3 \
+ dnsjit.core.object.dns.label.3 dnsjit.core.object.dns.q.3 \
+ dnsjit.core.object.dns.rr.3 dnsjit.core.object.ether.3 \
+ dnsjit.core.object.gre.3 dnsjit.core.object.icmp.3 \
+ dnsjit.core.object.icmp6.3 dnsjit.core.object.ieee802.3 \
+ dnsjit.core.object.ip.3 dnsjit.core.object.ip6.3 \
+ dnsjit.core.object.linuxsll.3 dnsjit.core.object.loop.3 \
+ dnsjit.core.object.null.3 dnsjit.core.object.payload.3 \
+ dnsjit.core.object.pcap.3 dnsjit.core.objects.3 \
+ dnsjit.core.object.tcp.3 dnsjit.core.object.udp.3 \
+ dnsjit.core.producer.3 dnsjit.core.receiver.3 \
+ dnsjit.core.thread.3 dnsjit.core.timespec.3 \
+ dnsjit.filter.copy.3 dnsjit.filter.ipsplit.3 \
+ dnsjit.filter.layer.3 dnsjit.filter.split.3 \
+ dnsjit.filter.timing.3 dnsjit.input.fpcap.3 \
+ dnsjit.input.mmpcap.3 dnsjit.input.pcap.3 dnsjit.input.zero.3 \
+ dnsjit.input.zpcap.3 dnsjit.lib.base64url.3 dnsjit.lib.clock.3 \
+ dnsjit.lib.getopt.3 dnsjit.lib.ip.3 dnsjit.lib.parseconf.3 \
+ dnsjit.lib.trie.3 dnsjit.lib.trie.iter.3 \
+ dnsjit.lib.trie.node.3 dnsjit.output.dnscli.3 \
+ dnsjit.output.null.3 dnsjit.output.pcap.3 \
+ dnsjit.output.respdiff.3 dnsjit.output.tcpcli.3 \
+ dnsjit.output.tlscli.3 dnsjit.output.udpcli.3
+all: $(BUILT_SOURCES) config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .1 .1in .3 .3in .c .hh .lo .lua .luah .luaho .luao .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+ @test -f $@ || rm -f stamp-h1
+ @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+ @rm -f stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status src/config.h
+$(srcdir)/config.h.in: $(am__configure_deps)
+ ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+ rm -f stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f config.h stamp-h1
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+core/$(am__dirstamp):
+ @$(MKDIR_P) core
+ @: > core/$(am__dirstamp)
+core/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) core/$(DEPDIR)
+ @: > core/$(DEPDIR)/$(am__dirstamp)
+core/channel.$(OBJEXT): core/$(am__dirstamp) \
+ core/$(DEPDIR)/$(am__dirstamp)
+core/compat.$(OBJEXT): core/$(am__dirstamp) \
+ core/$(DEPDIR)/$(am__dirstamp)
+core/file.$(OBJEXT): core/$(am__dirstamp) \
+ core/$(DEPDIR)/$(am__dirstamp)
+core/log.$(OBJEXT): core/$(am__dirstamp) \
+ core/$(DEPDIR)/$(am__dirstamp)
+core/object.$(OBJEXT): core/$(am__dirstamp) \
+ core/$(DEPDIR)/$(am__dirstamp)
+core/object/$(am__dirstamp):
+ @$(MKDIR_P) core/object
+ @: > core/object/$(am__dirstamp)
+core/object/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) core/object/$(DEPDIR)
+ @: > core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/dns.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/ether.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/gre.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/icmp6.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/icmp.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/ieee802.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/ip6.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/ip.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/linuxsll.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/loop.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/null.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/payload.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/pcap.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/tcp.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/object/udp.$(OBJEXT): core/object/$(am__dirstamp) \
+ core/object/$(DEPDIR)/$(am__dirstamp)
+core/producer.$(OBJEXT): core/$(am__dirstamp) \
+ core/$(DEPDIR)/$(am__dirstamp)
+core/receiver.$(OBJEXT): core/$(am__dirstamp) \
+ core/$(DEPDIR)/$(am__dirstamp)
+core/thread.$(OBJEXT): core/$(am__dirstamp) \
+ core/$(DEPDIR)/$(am__dirstamp)
+filter/$(am__dirstamp):
+ @$(MKDIR_P) filter
+ @: > filter/$(am__dirstamp)
+filter/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) filter/$(DEPDIR)
+ @: > filter/$(DEPDIR)/$(am__dirstamp)
+filter/copy.$(OBJEXT): filter/$(am__dirstamp) \
+ filter/$(DEPDIR)/$(am__dirstamp)
+filter/ipsplit.$(OBJEXT): filter/$(am__dirstamp) \
+ filter/$(DEPDIR)/$(am__dirstamp)
+filter/layer.$(OBJEXT): filter/$(am__dirstamp) \
+ filter/$(DEPDIR)/$(am__dirstamp)
+filter/split.$(OBJEXT): filter/$(am__dirstamp) \
+ filter/$(DEPDIR)/$(am__dirstamp)
+filter/timing.$(OBJEXT): filter/$(am__dirstamp) \
+ filter/$(DEPDIR)/$(am__dirstamp)
+input/$(am__dirstamp):
+ @$(MKDIR_P) input
+ @: > input/$(am__dirstamp)
+input/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) input/$(DEPDIR)
+ @: > input/$(DEPDIR)/$(am__dirstamp)
+input/fpcap.$(OBJEXT): input/$(am__dirstamp) \
+ input/$(DEPDIR)/$(am__dirstamp)
+input/mmpcap.$(OBJEXT): input/$(am__dirstamp) \
+ input/$(DEPDIR)/$(am__dirstamp)
+input/pcap.$(OBJEXT): input/$(am__dirstamp) \
+ input/$(DEPDIR)/$(am__dirstamp)
+input/zpcap.$(OBJEXT): input/$(am__dirstamp) \
+ input/$(DEPDIR)/$(am__dirstamp)
+lib/$(am__dirstamp):
+ @$(MKDIR_P) lib
+ @: > lib/$(am__dirstamp)
+lib/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) lib/$(DEPDIR)
+ @: > lib/$(DEPDIR)/$(am__dirstamp)
+lib/base64url.$(OBJEXT): lib/$(am__dirstamp) \
+ lib/$(DEPDIR)/$(am__dirstamp)
+lib/clock.$(OBJEXT): lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+lib/trie.$(OBJEXT): lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+output/$(am__dirstamp):
+ @$(MKDIR_P) output
+ @: > output/$(am__dirstamp)
+output/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) output/$(DEPDIR)
+ @: > output/$(DEPDIR)/$(am__dirstamp)
+output/dnscli.$(OBJEXT): output/$(am__dirstamp) \
+ output/$(DEPDIR)/$(am__dirstamp)
+output/pcap.$(OBJEXT): output/$(am__dirstamp) \
+ output/$(DEPDIR)/$(am__dirstamp)
+output/respdiff.$(OBJEXT): output/$(am__dirstamp) \
+ output/$(DEPDIR)/$(am__dirstamp)
+output/tcpcli.$(OBJEXT): output/$(am__dirstamp) \
+ output/$(DEPDIR)/$(am__dirstamp)
+output/tlscli.$(OBJEXT): output/$(am__dirstamp) \
+ output/$(DEPDIR)/$(am__dirstamp)
+output/udpcli.$(OBJEXT): output/$(am__dirstamp) \
+ output/$(DEPDIR)/$(am__dirstamp)
+
+dnsjit$(EXEEXT): $(dnsjit_OBJECTS) $(dnsjit_DEPENDENCIES) $(EXTRA_dnsjit_DEPENDENCIES)
+ @rm -f dnsjit$(EXEEXT)
+ $(AM_V_CCLD)$(dnsjit_LINK) $(dnsjit_OBJECTS) $(dnsjit_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+ -rm -f core/*.$(OBJEXT)
+ -rm -f core/object/*.$(OBJEXT)
+ -rm -f filter/*.$(OBJEXT)
+ -rm -f input/*.$(OBJEXT)
+ -rm -f lib/*.$(OBJEXT)
+ -rm -f output/*.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dnsjit.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/globals.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/$(DEPDIR)/channel.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/$(DEPDIR)/compat.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/$(DEPDIR)/file.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/$(DEPDIR)/log.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/$(DEPDIR)/object.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/$(DEPDIR)/producer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/$(DEPDIR)/receiver.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/$(DEPDIR)/thread.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/dns.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/ether.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/gre.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/icmp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/icmp6.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/ieee802.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/ip.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/ip6.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/linuxsll.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/loop.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/null.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/payload.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/pcap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/tcp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@core/object/$(DEPDIR)/udp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@filter/$(DEPDIR)/copy.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@filter/$(DEPDIR)/ipsplit.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@filter/$(DEPDIR)/layer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@filter/$(DEPDIR)/split.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@filter/$(DEPDIR)/timing.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/fpcap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/mmpcap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/pcap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/zpcap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/base64url.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/clock.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/trie.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@output/$(DEPDIR)/dnscli.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@output/$(DEPDIR)/pcap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@output/$(DEPDIR)/respdiff.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@output/$(DEPDIR)/tcpcli.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@output/$(DEPDIR)/tlscli.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@output/$(DEPDIR)/udpcli.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man1: $(man1_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man1_MANS)'; \
+ list2=''; \
+ test -n "$(man1dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.1[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man1:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man1_MANS)'; test -n "$(man1dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir)
+install-man3: $(man3_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man3_MANS)'; \
+ list2=''; \
+ test -n "$(man3dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man3dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man3dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.3[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man3dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man3dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man3dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man3dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man3:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man3_MANS)'; test -n "$(man3dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man3dir)'; $(am__uninstall_files_from_dir)
+install-nobase_dnsjitincludeHEADERS: $(nobase_dnsjitinclude_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(nobase_dnsjitinclude_HEADERS)'; test -n "$(dnsjitincludedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(dnsjitincludedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(dnsjitincludedir)" || exit 1; \
+ fi; \
+ $(am__nobase_list) | while read dir files; do \
+ xfiles=; for file in $$files; do \
+ if test -f "$$file"; then xfiles="$$xfiles $$file"; \
+ else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \
+ test -z "$$xfiles" || { \
+ test "x$$dir" = x. || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(dnsjitincludedir)/$$dir'"; \
+ $(MKDIR_P) "$(DESTDIR)$(dnsjitincludedir)/$$dir"; }; \
+ echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(dnsjitincludedir)/$$dir'"; \
+ $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(dnsjitincludedir)/$$dir" || exit $$?; }; \
+ done
+
+uninstall-nobase_dnsjitincludeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(nobase_dnsjitinclude_HEADERS)'; test -n "$(dnsjitincludedir)" || list=; \
+ $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \
+ dir='$(DESTDIR)$(dnsjitincludedir)'; $(am__uninstall_files_from_dir)
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+gcov-local:
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-recursive
+all-am: Makefile $(PROGRAMS) $(MANS) $(HEADERS) config.h
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man3dir)" "$(DESTDIR)$(dnsjitincludedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -rm -f core/$(DEPDIR)/$(am__dirstamp)
+ -rm -f core/$(am__dirstamp)
+ -rm -f core/object/$(DEPDIR)/$(am__dirstamp)
+ -rm -f core/object/$(am__dirstamp)
+ -rm -f filter/$(DEPDIR)/$(am__dirstamp)
+ -rm -f filter/$(am__dirstamp)
+ -rm -f input/$(DEPDIR)/$(am__dirstamp)
+ -rm -f input/$(am__dirstamp)
+ -rm -f lib/$(DEPDIR)/$(am__dirstamp)
+ -rm -f lib/$(am__dirstamp)
+ -rm -f output/$(DEPDIR)/$(am__dirstamp)
+ -rm -f output/$(am__dirstamp)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+@ENABLE_GCOV_FALSE@gcov-local:
+clean: clean-recursive
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/dnsjit.Po
+ -rm -f ./$(DEPDIR)/globals.Po
+ -rm -f core/$(DEPDIR)/channel.Po
+ -rm -f core/$(DEPDIR)/compat.Po
+ -rm -f core/$(DEPDIR)/file.Po
+ -rm -f core/$(DEPDIR)/log.Po
+ -rm -f core/$(DEPDIR)/object.Po
+ -rm -f core/$(DEPDIR)/producer.Po
+ -rm -f core/$(DEPDIR)/receiver.Po
+ -rm -f core/$(DEPDIR)/thread.Po
+ -rm -f core/object/$(DEPDIR)/dns.Po
+ -rm -f core/object/$(DEPDIR)/ether.Po
+ -rm -f core/object/$(DEPDIR)/gre.Po
+ -rm -f core/object/$(DEPDIR)/icmp.Po
+ -rm -f core/object/$(DEPDIR)/icmp6.Po
+ -rm -f core/object/$(DEPDIR)/ieee802.Po
+ -rm -f core/object/$(DEPDIR)/ip.Po
+ -rm -f core/object/$(DEPDIR)/ip6.Po
+ -rm -f core/object/$(DEPDIR)/linuxsll.Po
+ -rm -f core/object/$(DEPDIR)/loop.Po
+ -rm -f core/object/$(DEPDIR)/null.Po
+ -rm -f core/object/$(DEPDIR)/payload.Po
+ -rm -f core/object/$(DEPDIR)/pcap.Po
+ -rm -f core/object/$(DEPDIR)/tcp.Po
+ -rm -f core/object/$(DEPDIR)/udp.Po
+ -rm -f filter/$(DEPDIR)/copy.Po
+ -rm -f filter/$(DEPDIR)/ipsplit.Po
+ -rm -f filter/$(DEPDIR)/layer.Po
+ -rm -f filter/$(DEPDIR)/split.Po
+ -rm -f filter/$(DEPDIR)/timing.Po
+ -rm -f input/$(DEPDIR)/fpcap.Po
+ -rm -f input/$(DEPDIR)/mmpcap.Po
+ -rm -f input/$(DEPDIR)/pcap.Po
+ -rm -f input/$(DEPDIR)/zpcap.Po
+ -rm -f lib/$(DEPDIR)/base64url.Po
+ -rm -f lib/$(DEPDIR)/clock.Po
+ -rm -f lib/$(DEPDIR)/trie.Po
+ -rm -f output/$(DEPDIR)/dnscli.Po
+ -rm -f output/$(DEPDIR)/pcap.Po
+ -rm -f output/$(DEPDIR)/respdiff.Po
+ -rm -f output/$(DEPDIR)/tcpcli.Po
+ -rm -f output/$(DEPDIR)/tlscli.Po
+ -rm -f output/$(DEPDIR)/udpcli.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-hdr distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+gcov: gcov-recursive
+
+gcov-am: gcov-local
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-man install-nobase_dnsjitincludeHEADERS
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man: install-man1 install-man3
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/dnsjit.Po
+ -rm -f ./$(DEPDIR)/globals.Po
+ -rm -f core/$(DEPDIR)/channel.Po
+ -rm -f core/$(DEPDIR)/compat.Po
+ -rm -f core/$(DEPDIR)/file.Po
+ -rm -f core/$(DEPDIR)/log.Po
+ -rm -f core/$(DEPDIR)/object.Po
+ -rm -f core/$(DEPDIR)/producer.Po
+ -rm -f core/$(DEPDIR)/receiver.Po
+ -rm -f core/$(DEPDIR)/thread.Po
+ -rm -f core/object/$(DEPDIR)/dns.Po
+ -rm -f core/object/$(DEPDIR)/ether.Po
+ -rm -f core/object/$(DEPDIR)/gre.Po
+ -rm -f core/object/$(DEPDIR)/icmp.Po
+ -rm -f core/object/$(DEPDIR)/icmp6.Po
+ -rm -f core/object/$(DEPDIR)/ieee802.Po
+ -rm -f core/object/$(DEPDIR)/ip.Po
+ -rm -f core/object/$(DEPDIR)/ip6.Po
+ -rm -f core/object/$(DEPDIR)/linuxsll.Po
+ -rm -f core/object/$(DEPDIR)/loop.Po
+ -rm -f core/object/$(DEPDIR)/null.Po
+ -rm -f core/object/$(DEPDIR)/payload.Po
+ -rm -f core/object/$(DEPDIR)/pcap.Po
+ -rm -f core/object/$(DEPDIR)/tcp.Po
+ -rm -f core/object/$(DEPDIR)/udp.Po
+ -rm -f filter/$(DEPDIR)/copy.Po
+ -rm -f filter/$(DEPDIR)/ipsplit.Po
+ -rm -f filter/$(DEPDIR)/layer.Po
+ -rm -f filter/$(DEPDIR)/split.Po
+ -rm -f filter/$(DEPDIR)/timing.Po
+ -rm -f input/$(DEPDIR)/fpcap.Po
+ -rm -f input/$(DEPDIR)/mmpcap.Po
+ -rm -f input/$(DEPDIR)/pcap.Po
+ -rm -f input/$(DEPDIR)/zpcap.Po
+ -rm -f lib/$(DEPDIR)/base64url.Po
+ -rm -f lib/$(DEPDIR)/clock.Po
+ -rm -f lib/$(DEPDIR)/trie.Po
+ -rm -f output/$(DEPDIR)/dnscli.Po
+ -rm -f output/$(DEPDIR)/pcap.Po
+ -rm -f output/$(DEPDIR)/respdiff.Po
+ -rm -f output/$(DEPDIR)/tcpcli.Po
+ -rm -f output/$(DEPDIR)/tlscli.Po
+ -rm -f output/$(DEPDIR)/udpcli.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-man \
+ uninstall-nobase_dnsjitincludeHEADERS
+
+uninstall-man: uninstall-man1 uninstall-man3
+
+.MAKE: $(am__recursive_targets) all check install install-am \
+ install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libtool cscopelist-am ctags ctags-am \
+ distclean distclean-compile distclean-generic distclean-hdr \
+ distclean-libtool distclean-tags distdir dvi dvi-am gcov-am \
+ gcov-local html html-am info info-am install install-am \
+ install-binPROGRAMS install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-man1 install-man3 install-nobase_dnsjitincludeHEADERS \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-binPROGRAMS uninstall-man \
+ uninstall-man1 uninstall-man3 \
+ uninstall-nobase_dnsjitincludeHEADERS
+
+.PRECIOUS: Makefile
+
+
+.lua.luao:
+ @mkdir -p `dirname "$@"`
+ $(LUAJIT) -bg -n "dnsjit.`echo \"$@\" | sed 's%\..*%%' | sed 's%/%.%g'`" -t o "$<" "$@"
+
+.luah.luaho:
+ @mkdir -p `dirname "$@"`
+ $(LUAJIT) -bg -n "dnsjit.`echo \"$@\" | sed 's%\..*%%' | sed 's%/%.%g'`_h" -t o "$<" "$@"
+
+.hh.luah:
+ @mkdir -p `dirname "$@"`
+ @echo 'module(...,package.seeall);' > "$@"
+ @cat "$<" | grep '^//lua:' | sed 's%^//lua:%%' >> "$@"
+ @echo 'require("ffi").cdef[[' >> "$@"
+ @cat "$<" | grep -v '^#' >> "$@"
+ @echo ']]' >> "$@"
+
+.1in.1:
+ sed -e 's,[@]PACKAGE_VERSION[@],$(PACKAGE_VERSION),g' \
+ -e 's,[@]PACKAGE_URL[@],$(PACKAGE_URL),g' \
+ -e 's,[@]PACKAGE_BUGREPORT[@],$(PACKAGE_BUGREPORT),g' \
+ < "$<" > "$@"
+
+.3in.3:
+ sed -e 's,[@]PACKAGE_VERSION[@],$(PACKAGE_VERSION),g' \
+ -e 's,[@]PACKAGE_URL[@],$(PACKAGE_URL),g' \
+ -e 's,[@]PACKAGE_BUGREPORT[@],$(PACKAGE_BUGREPORT),g' \
+ < "$<" > "$@"
+
+@ENABLE_GCOV_TRUE@gcov-local:
+@ENABLE_GCOV_TRUE@ for src in $(dnsjit_SOURCES); do \
+@ENABLE_GCOV_TRUE@ gcov -x -l -r -s "$(srcdir)" "$$src"; \
+@ENABLE_GCOV_TRUE@ done
+
+core/compat.hh: gen-compat.lua
+ $(LUAJIT) "$(srcdir)/gen-compat.lua" > "$@"
+
+core/log_errstr.c: gen-errno.sh
+ "$(srcdir)/gen-errno.sh" > "$@"
+
+dnsjit.core.3in: core.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core.lua" > "$@"
+
+dnsjit.lib.3in: lib.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib.lua" > "$@"
+
+dnsjit.input.3in: input.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/input.lua" > "$@"
+
+dnsjit.filter.3in: filter.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/filter.lua" > "$@"
+
+dnsjit.output.3in: output.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output.lua" > "$@"
+
+dnsjit.core.channel.3in: core/channel.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/channel.lua" > "$@"
+
+dnsjit.core.compat.3in: core/compat.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/compat.lua" > "$@"
+
+dnsjit.core.file.3in: core/file.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/file.lua" > "$@"
+
+dnsjit.core.loader.3in: core/loader.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/loader.lua" > "$@"
+
+dnsjit.core.log.3in: core/log.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/log.lua" > "$@"
+
+dnsjit.core.object.dns.label.3in: core/object/dns/label.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/dns/label.lua" > "$@"
+
+dnsjit.core.object.dns.3in: core/object/dns.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/dns.lua" > "$@"
+
+dnsjit.core.object.dns.q.3in: core/object/dns/q.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/dns/q.lua" > "$@"
+
+dnsjit.core.object.dns.rr.3in: core/object/dns/rr.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/dns/rr.lua" > "$@"
+
+dnsjit.core.object.ether.3in: core/object/ether.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/ether.lua" > "$@"
+
+dnsjit.core.object.gre.3in: core/object/gre.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/gre.lua" > "$@"
+
+dnsjit.core.object.icmp6.3in: core/object/icmp6.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/icmp6.lua" > "$@"
+
+dnsjit.core.object.icmp.3in: core/object/icmp.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/icmp.lua" > "$@"
+
+dnsjit.core.object.ieee802.3in: core/object/ieee802.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/ieee802.lua" > "$@"
+
+dnsjit.core.object.ip6.3in: core/object/ip6.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/ip6.lua" > "$@"
+
+dnsjit.core.object.ip.3in: core/object/ip.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/ip.lua" > "$@"
+
+dnsjit.core.object.linuxsll.3in: core/object/linuxsll.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/linuxsll.lua" > "$@"
+
+dnsjit.core.object.loop.3in: core/object/loop.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/loop.lua" > "$@"
+
+dnsjit.core.object.3in: core/object.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object.lua" > "$@"
+
+dnsjit.core.object.null.3in: core/object/null.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/null.lua" > "$@"
+
+dnsjit.core.object.payload.3in: core/object/payload.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/payload.lua" > "$@"
+
+dnsjit.core.object.pcap.3in: core/object/pcap.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/pcap.lua" > "$@"
+
+dnsjit.core.objects.3in: core/objects.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/objects.lua" > "$@"
+
+dnsjit.core.object.tcp.3in: core/object/tcp.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/tcp.lua" > "$@"
+
+dnsjit.core.object.udp.3in: core/object/udp.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/object/udp.lua" > "$@"
+
+dnsjit.core.producer.3in: core/producer.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/producer.lua" > "$@"
+
+dnsjit.core.receiver.3in: core/receiver.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/receiver.lua" > "$@"
+
+dnsjit.core.thread.3in: core/thread.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/thread.lua" > "$@"
+
+dnsjit.core.timespec.3in: core/timespec.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/core/timespec.lua" > "$@"
+
+dnsjit.filter.copy.3in: filter/copy.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/filter/copy.lua" > "$@"
+
+dnsjit.filter.ipsplit.3in: filter/ipsplit.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/filter/ipsplit.lua" > "$@"
+
+dnsjit.filter.layer.3in: filter/layer.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/filter/layer.lua" > "$@"
+
+dnsjit.filter.split.3in: filter/split.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/filter/split.lua" > "$@"
+
+dnsjit.filter.timing.3in: filter/timing.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/filter/timing.lua" > "$@"
+
+dnsjit.input.fpcap.3in: input/fpcap.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/input/fpcap.lua" > "$@"
+
+dnsjit.input.mmpcap.3in: input/mmpcap.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/input/mmpcap.lua" > "$@"
+
+dnsjit.input.pcap.3in: input/pcap.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/input/pcap.lua" > "$@"
+
+dnsjit.input.zero.3in: input/zero.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/input/zero.lua" > "$@"
+
+dnsjit.input.zpcap.3in: input/zpcap.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/input/zpcap.lua" > "$@"
+
+dnsjit.lib.base64url.3in: lib/base64url.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/base64url.lua" > "$@"
+
+dnsjit.lib.clock.3in: lib/clock.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/clock.lua" > "$@"
+
+dnsjit.lib.getopt.3in: lib/getopt.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/getopt.lua" > "$@"
+
+dnsjit.lib.ip.3in: lib/ip.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/ip.lua" > "$@"
+
+dnsjit.lib.parseconf.3in: lib/parseconf.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/parseconf.lua" > "$@"
+
+dnsjit.lib.trie.iter.3in: lib/trie/iter.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/trie/iter.lua" > "$@"
+
+dnsjit.lib.trie.3in: lib/trie.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/trie.lua" > "$@"
+
+dnsjit.lib.trie.node.3in: lib/trie/node.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/lib/trie/node.lua" > "$@"
+
+dnsjit.output.dnscli.3in: output/dnscli.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/dnscli.lua" > "$@"
+
+dnsjit.output.null.3in: output/null.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/null.lua" > "$@"
+
+dnsjit.output.pcap.3in: output/pcap.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/pcap.lua" > "$@"
+
+dnsjit.output.respdiff.3in: output/respdiff.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/respdiff.lua" > "$@"
+
+dnsjit.output.tcpcli.3in: output/tcpcli.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/tcpcli.lua" > "$@"
+
+dnsjit.output.tlscli.3in: output/tlscli.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/tlscli.lua" > "$@"
+
+dnsjit.output.udpcli.3in: output/udpcli.lua gen-manpage.lua
+ $(LUAJIT) "$(srcdir)/gen-manpage.lua" "$(srcdir)/output/udpcli.lua" > "$@"
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/include/dnsjit/config.h.in b/include/dnsjit/config.h.in
new file mode 100644
index 0000000..c156fa2
--- /dev/null
+++ b/include/dnsjit/config.h.in
@@ -0,0 +1,295 @@
+/* src/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to 1 to support Advanced Bit Manipulation */
+#undef HAVE_ABM
+
+/* Define to 1 to support Multi-Precision Add-Carry Instruction Extensions */
+#undef HAVE_ADX
+
+/* Define to 1 to support Advanced Encryption Standard New Instruction Set
+ (AES-NI) */
+#undef HAVE_AES
+
+/* Support Altivec instructions */
+#undef HAVE_ALTIVEC
+
+/* Define to 1 to support Advanced Vector Extensions */
+#undef HAVE_AVX
+
+/* Define to 1 to support Advanced Vector Extensions 2 */
+#undef HAVE_AVX2
+
+/* Define to 1 to support AVX-512 Byte and Word Instructions */
+#undef HAVE_AVX512_BW
+
+/* Define to 1 to support AVX-512 Conflict Detection Instructions */
+#undef HAVE_AVX512_CD
+
+/* Define to 1 to support AVX-512 Doubleword and Quadword Instructions */
+#undef HAVE_AVX512_DQ
+
+/* Define to 1 to support AVX-512 Exponential & Reciprocal Instructions */
+#undef HAVE_AVX512_ER
+
+/* Define to 1 to support AVX-512 Foundation Extensions */
+#undef HAVE_AVX512_F
+
+/* Define to 1 to support AVX-512 Integer Fused Multiply Add Instructions */
+#undef HAVE_AVX512_IFMA
+
+/* Define to 1 to support AVX-512 Conflict Prefetch Instructions */
+#undef HAVE_AVX512_PF
+
+/* Define to 1 to support AVX-512 Vector Byte Manipulation Instructions */
+#undef HAVE_AVX512_VBMI
+
+/* Define to 1 to support AVX-512 Vector Length Extensions */
+#undef HAVE_AVX512_VL
+
+/* Define to 1 to support Bit Manipulation Instruction Set 1 */
+#undef HAVE_BMI1
+
+/* Define to 1 to support Bit Manipulation Instruction Set 2 */
+#undef HAVE_BMI2
+
+/* Define to 1 if you have the <byteswap.h> header file. */
+#undef HAVE_BYTESWAP_H
+
+/* Define to 1 if you have the <ck_pr.h> header file. */
+#undef HAVE_CK_PR_H
+
+/* Define to 1 if you have the <ck_ring.h> header file. */
+#undef HAVE_CK_RING_H
+
+/* Define to 1 if you have the `clock_nanosleep' function. */
+#undef HAVE_CLOCK_NANOSLEEP
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <endian.h> header file. */
+#undef HAVE_ENDIAN_H
+
+/* Define to 1 to support Fused Multiply-Add Extensions 3 */
+#undef HAVE_FMA3
+
+/* Define to 1 to support Fused Multiply-Add Extensions 4 */
+#undef HAVE_FMA4
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `ck' library (-lck). */
+#undef HAVE_LIBCK
+
+/* Define to 1 if you have the `gnutls' library (-lgnutls). */
+#undef HAVE_LIBGNUTLS
+
+/* Define to 1 if you have the `lmdb' library (-llmdb). */
+#undef HAVE_LIBLMDB
+
+/* Define to 1 if you have the `pcap' library (-lpcap). */
+#undef HAVE_LIBPCAP
+
+/* Define to 1 if you have the <lmdb.h> header file. */
+#undef HAVE_LMDB_H
+
+/* Use liblz4 */
+#undef HAVE_LZ4
+
+/* Define to 1 if you have the <machine/endian.h> header file. */
+#undef HAVE_MACHINE_ENDIAN_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 to support Multimedia Extensions */
+#undef HAVE_MMX
+
+/* Define to 1 to support Memory Protection Extensions */
+#undef HAVE_MPX
+
+/* Define to 1 if you have the `nanosleep' function. */
+#undef HAVE_NANOSLEEP
+
+/* Define to 1 if you have the <net/ethernet.h> header file. */
+#undef HAVE_NET_ETHERNET_H
+
+/* Define to 1 if you have the <net/ethertypes.h> header file. */
+#undef HAVE_NET_ETHERTYPES_H
+
+/* Define to 1 if you have the `pcap_activate' function. */
+#undef HAVE_PCAP_ACTIVATE
+
+/* Define to 1 if you have the `pcap_create' function. */
+#undef HAVE_PCAP_CREATE
+
+/* Define to 1 if the system has the type `pcap_direction_t'. */
+#undef HAVE_PCAP_DIRECTION_T
+
+/* Define to 1 if you have the `pcap_open_offline_with_tstamp_precision'
+ function. */
+#undef HAVE_PCAP_OPEN_OFFLINE_WITH_TSTAMP_PRECISION
+
+/* Define to 1 if you have the `pcap_setdirection' function. */
+#undef HAVE_PCAP_SETDIRECTION
+
+/* Define to 1 if you have the `pcap_set_immediate_mode' function. */
+#undef HAVE_PCAP_SET_IMMEDIATE_MODE
+
+/* Define to 1 if you have the `pcap_set_tstamp_precision' function. */
+#undef HAVE_PCAP_SET_TSTAMP_PRECISION
+
+/* Define to 1 if you have the `pcap_set_tstamp_type' function. */
+#undef HAVE_PCAP_SET_TSTAMP_TYPE
+
+/* Define to 1 to support Prefetch Vector Data Into Caches WT1 */
+#undef HAVE_PREFETCHWT1
+
+/* Define if you have POSIX threads libraries and header files. */
+#undef HAVE_PTHREAD
+
+/* Have PTHREAD_PRIO_INHERIT. */
+#undef HAVE_PTHREAD_PRIO_INHERIT
+
+/* Define to 1 to support Digital Random Number Generator */
+#undef HAVE_RDRND
+
+/* Define to 1 if you have the `sched_yield' function. */
+#undef HAVE_SCHED_YIELD
+
+/* Define to 1 to support Secure Hash Algorithm Extension */
+#undef HAVE_SHA
+
+/* Define to 1 to support Streaming SIMD Extensions */
+#undef HAVE_SSE
+
+/* Define to 1 to support Streaming SIMD Extensions */
+#undef HAVE_SSE2
+
+/* Define to 1 to support Streaming SIMD Extensions 3 */
+#undef HAVE_SSE3
+
+/* Define to 1 to support Streaming SIMD Extensions 4.1 */
+#undef HAVE_SSE4_1
+
+/* Define to 1 to support Streaming SIMD Extensions 4.2 */
+#undef HAVE_SSE4_2
+
+/* Define to 1 to support AMD Streaming SIMD Extensions 4a */
+#undef HAVE_SSE4a
+
+/* Define to 1 to support Supplemental Streaming SIMD Extensions 3 */
+#undef HAVE_SSSE3
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/endian.h> header file. */
+#undef HAVE_SYS_ENDIAN_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Support VSX instructions */
+#undef HAVE_VSX
+
+/* Define to 1 to support eXtended Operations Extensions */
+#undef HAVE_XOP
+
+/* Use libzstd */
+#undef HAVE_ZSTD
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#undef LT_OBJDIR
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the major version of this package. */
+#undef PACKAGE_MAJOR_VERSION
+
+/* Define to the minor version of this package. */
+#undef PACKAGE_MINOR_VERSION
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the patch version of this package. */
+#undef PACKAGE_PATCH_VERSION
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to necessary symbol if this constant uses a non-standard name on
+ your system. */
+#undef PTHREAD_CREATE_JOINABLE
+
+/* The size of `ck_ring_buffer_t', as computed by sizeof. */
+#undef SIZEOF_CK_RING_BUFFER_T
+
+/* The size of `ck_ring_t', as computed by sizeof. */
+#undef SIZEOF_CK_RING_T
+
+/* The size of `gnutls_certificate_credentials_t', as computed by sizeof. */
+#undef SIZEOF_GNUTLS_CERTIFICATE_CREDENTIALS_T
+
+/* The size of `gnutls_session_t', as computed by sizeof. */
+#undef SIZEOF_GNUTLS_SESSION_T
+
+/* The size of `pthread_cond_t', as computed by sizeof. */
+#undef SIZEOF_PTHREAD_COND_T
+
+/* The size of `pthread_mutex_t', as computed by sizeof. */
+#undef SIZEOF_PTHREAD_MUTEX_T
+
+/* The size of `pthread_t', as computed by sizeof. */
+#undef SIZEOF_PTHREAD_T
+
+/* The size of `struct pollfd', as computed by sizeof. */
+#undef SIZEOF_STRUCT_POLLFD
+
+/* The size of `struct sockaddr_storage', as computed by sizeof. */
+#undef SIZEOF_STRUCT_SOCKADDR_STORAGE
+
+/* The size of `void*', as computed by sizeof. */
+#undef SIZEOF_VOIDP
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Version number of package */
+#undef VERSION
diff --git a/include/dnsjit/core.lua b/include/dnsjit/core.lua
new file mode 100644
index 0000000..47723e6
--- /dev/null
+++ b/include/dnsjit/core.lua
@@ -0,0 +1,45 @@
+-- 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.core
+-- Core modules for dnsjit
+--
+-- Core modules for handling things like logging, DNS messages and
+-- receiver/receive functionality.
+-- .SS Global Variables
+-- The following global variables exists in
+-- .IR dnsjit .
+-- .TP
+-- .B arg
+-- A table with the arguments given on the command line, the first will be
+-- the path to the
+-- .I dnsjit
+-- binary, second will be the path to the
+-- .IR script .
+module(...,package.seeall)
+
+-- dnsjit.core.channel (3),
+-- dnsjit.core.compat (3),
+-- dnsjit.core.log (3),
+-- dnsjit.core.object (3),
+-- dnsjit.core.objects (3),
+-- dnsjit.core.producer (3),
+-- dnsjit.core.receiver (3),
+-- dnsjit.core.thread (3),
+-- dnsjit.core.timespec (3)
+return
diff --git a/include/dnsjit/core/assert.h b/include/dnsjit/core/assert.h
new file mode 100644
index 0000000..1cdc6e6
--- /dev/null
+++ b/include/dnsjit/core/assert.h
@@ -0,0 +1,46 @@
+/*
+ * 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/>.
+ */
+
+#ifndef __dnsjit_core_assert_h
+#define __dnsjit_core_assert_h
+
+#include <dnsjit/core/log.h>
+
+#define mlassert_self() \
+ if (!self) \
+ core_log_fatal(&_log, __FILE__, __LINE__, "self is nil")
+#define glassert_self() \
+ if (!self) \
+ core_log_fatal(0, __FILE__, __LINE__, "self is nil")
+
+#define lassert(expression, msg...) \
+ if (!(expression)) \
+ core_log_fatal(&self->_log, __FILE__, __LINE__, msg)
+#define lpassert(expression, msg...) \
+ if (!(expression)) \
+ core_log_fatal(self->_log, __FILE__, __LINE__, msg)
+#define mlassert(expression, msg...) \
+ if (!(expression)) \
+ core_log_fatal(&_log, __FILE__, __LINE__, msg)
+#define glassert(expression, msg...) \
+ if (!(expression)) \
+ core_log_fatal(0, __FILE__, __LINE__, msg)
+
+#endif
diff --git a/include/dnsjit/core/channel.c b/include/dnsjit/core/channel.c
new file mode 100644
index 0000000..691ad1a
--- /dev/null
+++ b/include/dnsjit/core/channel.c
@@ -0,0 +1,169 @@
+/*
+ * 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 "core/channel.h"
+#include "core/assert.h"
+
+#include <sched.h>
+
+static core_log_t _log = LOG_T_INIT("core.channel");
+static core_channel_t _defaults = {
+ LOG_T_INIT_OBJ("core.channel"),
+ 0, { 0 }, 0, 0,
+ 0, 0
+};
+
+core_log_t* core_channel_log()
+{
+ return &_log;
+}
+
+static inline bool _is_pow2(size_t num)
+{
+ while (num != 1) {
+ if (num % 2 != 0)
+ return false;
+ num = num / 2;
+ }
+ return true;
+}
+
+void core_channel_init(core_channel_t* self, size_t capacity)
+{
+ mlassert_self();
+ if (capacity < 4 || !_is_pow2(capacity)) {
+ mlfatal("invalid capacity");
+ }
+
+ *self = _defaults;
+ self->capacity = capacity;
+
+ lfatal_oom(self->ring_buf = malloc(sizeof(ck_ring_buffer_t) * capacity));
+ ck_ring_init(&self->ring, capacity);
+}
+
+void core_channel_destroy(core_channel_t* self)
+{
+ mlassert_self();
+ free(self->ring_buf);
+}
+
+void core_channel_put(core_channel_t* self, const void* obj)
+{
+ mlassert_self();
+ lassert(self->ring_buf, "ring_buf is nil");
+
+ while (!ck_ring_enqueue_spsc(&self->ring, self->ring_buf, (void*)obj)) {
+ sched_yield();
+ }
+}
+
+int core_channel_try_put(core_channel_t* self, const void* obj)
+{
+ mlassert_self();
+ lassert(self->ring_buf, "ring_buf is nil");
+
+ if (!ck_ring_enqueue_spsc(&self->ring, self->ring_buf, (void*)obj)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void* core_channel_get(core_channel_t* self)
+{
+ void* obj = 0;
+ mlassert_self();
+ lassert(self->ring_buf, "ring_buf is nil");
+
+ while (!ck_ring_dequeue_spsc(&self->ring, self->ring_buf, &obj)) {
+ sched_yield();
+ if (ck_pr_load_int(&self->closed)) {
+ linfo("channel closed");
+ return 0;
+ }
+ }
+
+ return obj;
+}
+
+void* core_channel_try_get(core_channel_t* self)
+{
+ void* obj = 0;
+ mlassert_self();
+ lassert(self->ring_buf, "ring_buf is nil");
+
+ if (!ck_ring_dequeue_spsc(&self->ring, self->ring_buf, &obj)) {
+ return 0;
+ }
+
+ return obj;
+}
+
+int core_channel_size(core_channel_t* self)
+{
+ mlassert_self();
+ return ck_ring_size(&self->ring);
+}
+
+bool core_channel_full(core_channel_t* self)
+{
+ mlassert_self();
+
+ /* ck_ring can only hold capacity minus one enties at a time */
+ if (ck_ring_size(&self->ring) < (self->capacity - 1)) {
+ return false;
+ }
+ return true;
+}
+
+void core_channel_close(core_channel_t* self)
+{
+ mlassert_self();
+ ck_pr_store_int(&self->closed, 1);
+}
+
+core_receiver_t core_channel_receiver()
+{
+ return (core_receiver_t)core_channel_put;
+}
+
+void core_channel_run(core_channel_t* self)
+{
+ void* obj = 0;
+ mlassert_self();
+ lassert(self->ring_buf, "ring_buf is nil");
+ if (!self->recv) {
+ lfatal("no receiver set");
+ }
+
+ for (;;) {
+ while (!ck_ring_dequeue_spsc(&self->ring, self->ring_buf, &obj)) {
+ sched_yield();
+ if (ck_pr_load_int(&self->closed)) {
+ linfo("channel closed");
+ return;
+ }
+ }
+ self->recv(self->ctx, obj);
+ }
+}
diff --git a/include/dnsjit/core/channel.h b/include/dnsjit/core/channel.h
new file mode 100644
index 0000000..b0e90ae
--- /dev/null
+++ b/include/dnsjit/core/channel.h
@@ -0,0 +1,41 @@
+/*
+ * 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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+
+#ifndef __dnsjit_core_channel_h
+#define __dnsjit_core_channel_h
+
+#if defined(__GNUC__) || defined(__SUNPRO_C)
+#include "gcc/ck_cc.h"
+#ifdef CK_CC_RESTRICT
+#undef CK_CC_RESTRICT
+#define CK_CC_RESTRICT __restrict__
+#endif
+#endif
+
+#include <ck_ring.h>
+#include <ck_pr.h>
+#include <stdbool.h>
+
+#include <dnsjit/core/channel.hh>
+
+#endif
diff --git a/include/dnsjit/core/channel.hh b/include/dnsjit/core/channel.hh
new file mode 100644
index 0000000..e6ec0e0
--- /dev/null
+++ b/include/dnsjit/core/channel.hh
@@ -0,0 +1,49 @@
+/*
+ * 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.compat_h")
+//lua:require("dnsjit.core.log")
+//lua:require("dnsjit.core.receiver_h")
+
+typedef struct core_channel {
+ core_log_t _log;
+ ck_ring_buffer_t* ring_buf;
+ ck_ring_t ring;
+ int closed;
+ size_t capacity;
+
+ core_receiver_t recv;
+ void* ctx;
+} core_channel_t;
+
+core_log_t* core_channel_log();
+
+void core_channel_init(core_channel_t* self, size_t capacity);
+void core_channel_destroy(core_channel_t* self);
+void core_channel_put(core_channel_t* self, const void* obj);
+int core_channel_try_put(core_channel_t* self, const void* obj);
+void* core_channel_get(core_channel_t* self);
+void* core_channel_try_get(core_channel_t* self);
+int core_channel_size(core_channel_t* self);
+bool core_channel_full(core_channel_t* self);
+void core_channel_close(core_channel_t* self);
+
+core_receiver_t core_channel_receiver();
+void core_channel_run(core_channel_t* self);
diff --git a/include/dnsjit/core/channel.lua b/include/dnsjit/core/channel.lua
new file mode 100644
index 0000000..b837cc4
--- /dev/null
+++ b/include/dnsjit/core/channel.lua
@@ -0,0 +1,142 @@
+-- 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.core.channel
+-- Send data to another thread
+-- local chan = require("dnsjit.core.channel").new()
+-- local thr = require("dnsjit.core.thread").new()
+-- thr:start(function(thr)
+-- local chan = thr:pop()
+-- local obj = chan:get()
+-- ...
+-- end)
+-- thr:push(chan)
+-- chan:put(...)
+-- chan:close()
+-- thr:stop()
+--
+-- A channel can be used to send data to another thread, this is done by
+-- putting a pointer to the data into a wait-free and lock-free ring buffer
+-- (concurrency kit).
+-- The channel uses the single producer, single consumer model (SPSC) so
+-- there can only be one writer and one reader.
+-- .SS Attributes
+-- .TP
+-- int closed
+-- Is 1 if the channel has been closed.
+module(...,package.seeall)
+
+require("dnsjit.core.channel_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_channel_t"
+local core_channel_t
+local Channel = {}
+
+-- Create a new Channel, use the optional
+-- .I capacity
+-- to specify the capacity of the channel (buffer).
+-- Capacity must be a power-of-two greater than or equal to 4.
+-- Default capacity is 2048.
+function Channel.new(capacity)
+ if capacity == nil then
+ capacity = 2048
+ end
+ local self = core_channel_t()
+ C.core_channel_init(self, capacity)
+ ffi.gc(self, C.core_channel_destroy)
+ return self
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Channel:log()
+ if self == nil then
+ return C.core_channel_log()
+ end
+ return self._log
+end
+
+-- Return information to use when sharing this object between threads.
+function Channel:share()
+ return ffi.cast("void*", self), t_name.."*", "dnsjit.core.channel"
+end
+
+-- Put an object into the channel, if the channel is full then it will
+-- stall and wait until space becomes available.
+-- Object may be nil.
+function Channel:put(obj)
+ C.core_channel_put(self, obj)
+end
+
+-- Try and put an object into the channel.
+-- Returns 0 on success.
+function Channel:put(obj)
+ C.core_channel_try_put(self, obj)
+end
+
+-- Get an object from the channel, if the channel is empty it will wait until
+-- an object is available.
+-- Returns nil if the channel is closed or if a nil object was explicitly put
+-- into the channel.
+function Channel:get()
+ return C.core_channel_get(self)
+end
+
+-- Try and get an object from the channel.
+-- Returns nil if there was no objects to get.
+function Channel:try_get()
+ return C.core_channel_try_get(self)
+end
+
+-- Return number of enqueued objects.
+function Channel:size()
+ return C.core_channel_size(self)
+end
+
+-- Returns true when channel is full.
+function Channel:full()
+ return C.core_channel_full(self)
+end
+
+-- Close the channel.
+function Channel:close()
+ C.core_channel_close(self)
+end
+
+-- Return the C functions and context for receiving objects.
+function Channel:receive()
+ return C.core_channel_receiver(), self
+end
+
+-- Set the receiver to pass objects to.
+-- NOTE; The channel keeps no reference of the receiver, it needs to live as
+-- long as the channel does.
+function Channel:receiver(o)
+ self.recv, self.ctx = o:receive()
+end
+
+-- Retrieve all objects from the channel and send it to the receiver.
+function Channel:run()
+ C.core_channel_run(self)
+end
+
+core_channel_t = ffi.metatype(t_name, { __index = Channel })
+
+-- dnsjit.core.thread (3)
+return Channel
diff --git a/include/dnsjit/core/compat.c b/include/dnsjit/core/compat.c
new file mode 100644
index 0000000..9f2be98
--- /dev/null
+++ b/include/dnsjit/core/compat.c
@@ -0,0 +1,25 @@
+/*
+ * 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 <stdint.h>
+
+#include "core/compat.hh"
diff --git a/include/dnsjit/core/compat.h b/include/dnsjit/core/compat.h
new file mode 100644
index 0000000..c9486c3
--- /dev/null
+++ b/include/dnsjit/core/compat.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/>.
+ */
+
+#ifndef __dnsjit_core_compat_h
+#define __dnsjit_core_compat_h
+
+#include <unistd.h>
+
+#ifdef __ssize_t_defined
+typedef ssize_t luajit_ssize_t;
+#else
+typedef long luajit_ssize_t;
+#endif
+
+#endif
diff --git a/include/dnsjit/core/compat.lua b/include/dnsjit/core/compat.lua
new file mode 100644
index 0000000..bb6d45c
--- /dev/null
+++ b/include/dnsjit/core/compat.lua
@@ -0,0 +1,41 @@
+-- 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.core.compat
+-- Cross platform compatibility support
+-- require("dnsjit.core.compat_h")
+--
+-- This module defines various system structures so they can be exposed into
+-- Lua but not really used. The size is generated at compile time to match
+-- that of the structures on the platform.
+-- .SS Structures
+-- .TP
+-- pthread_t
+-- .TP
+-- pthread_cond_t
+-- .TP
+-- pthread_mutex_t
+-- .TP
+-- struct sockaddr_storage
+-- .TP
+-- ck_ring_t
+-- .TP
+-- ck_ring_buffer_t
+module(...,package.seeall)
+
+require("dnsjit.core.compat_h")
diff --git a/include/dnsjit/core/file.c b/include/dnsjit/core/file.c
new file mode 100644
index 0000000..d632a73
--- /dev/null
+++ b/include/dnsjit/core/file.c
@@ -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/>.
+ */
+
+#include "config.h"
+
+#include "core/file.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+int core_file_exists(const char* filename)
+{
+ struct stat s;
+
+ if (stat(filename, &s)) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/include/dnsjit/core/file.h b/include/dnsjit/core/file.h
new file mode 100644
index 0000000..ad5a174
--- /dev/null
+++ b/include/dnsjit/core/file.h
@@ -0,0 +1,26 @@
+/*
+ * 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/>.
+ */
+
+#ifndef __dnsjit_core_file_h
+#define __dnsjit_core_file_h
+
+#include <dnsjit/core/file.hh>
+
+#endif
diff --git a/include/dnsjit/core/file.hh b/include/dnsjit/core/file.hh
new file mode 100644
index 0000000..70b1bc9
--- /dev/null
+++ b/include/dnsjit/core/file.hh
@@ -0,0 +1,21 @@
+/*
+ * 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/>.
+ */
+
+int core_file_exists(const char*);
diff --git a/include/dnsjit/core/file.lua b/include/dnsjit/core/file.lua
new file mode 100644
index 0000000..5ce52d7
--- /dev/null
+++ b/include/dnsjit/core/file.lua
@@ -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/>.
+
+-- dnsjit.core.file
+-- OS file operations
+-- require("dnsjit.core.file")
+-- local ffi = require("ffi")
+-- if ffi.C.core_file_exists("path/file") == 0 then
+-- ...
+-- end
+--
+-- Module that exposes some file operations that are missing from Lua.
+-- .SS C functions
+-- .TP
+-- core_file_exists(path/filename)
+-- Function that takes a string and uses
+-- .B stat()
+-- to check if that path/filename exists.
+-- Returns zero if it exists.
+module(...,package.seeall)
+
+require("dnsjit.core.file_h")
diff --git a/include/dnsjit/core/loader.lua b/include/dnsjit/core/loader.lua
new file mode 100644
index 0000000..b56a390
--- /dev/null
+++ b/include/dnsjit/core/loader.lua
@@ -0,0 +1,68 @@
+-- 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.core.loader
+-- Dynamic library loader
+-- local loader = require("dnsjit.core.loader")
+-- loader.load("example-input-zero/zero")
+--
+-- Module for loading dynamic libraries (.so) in more ways then LuaJIT can.
+-- This is mainly used in external modules.
+module(...,package.seeall)
+
+require("dnsjit.core.file")
+local ffi = require("ffi")
+local C = ffi.C
+
+local Loader = {}
+
+-- Search
+-- .B package.cpath
+-- for the given name and load the first found.
+-- If
+-- .B global
+-- is true (default true if not given) then the loaded symbols will also
+-- be available globally.
+-- Returns the loaded C library as per
+-- .BR ffi.load() .
+-- .br
+-- The
+-- .B ?
+-- in each path of
+-- .B package.cpath
+-- will be replace by the given name, so usually the ".so" part of the
+-- library does not need to be given.
+-- See
+-- .I package.cpath
+-- and
+-- .I package.loaders
+-- in Lua 5.1 for more information.
+function Loader.load(name, global)
+ if global ~= false then
+ global = true
+ end
+ for path in string.gmatch(package.cpath, "[^;]+") do
+ path = path:gsub("?", name)
+ if C.core_file_exists(path) == 0 then
+ return ffi.load(path, global)
+ end
+ end
+ return ffi.load(name)
+end
+
+return Loader
diff --git a/include/dnsjit/core/log.c b/include/dnsjit/core/log.c
new file mode 100644
index 0000000..a334e8e
--- /dev/null
+++ b/include/dnsjit/core/log.c
@@ -0,0 +1,399 @@
+/*
+ * 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 "core/log.h"
+
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+static core_log_t _log = LOG_T_INIT("core");
+
+void core_log_debug(const core_log_t* l, const char* file, size_t line, const char* msg, ...)
+{
+ char buf[512];
+ va_list ap;
+ if (!l) {
+ if (_log.settings.debug != 3) {
+ return;
+ }
+ } else {
+ if (l->settings.debug) {
+ if (l->settings.debug != 3) {
+ return;
+ }
+ } else if (l->module && l->module->debug) {
+ if (l->module->debug != 3) {
+ return;
+ }
+ } else if (_log.settings.debug != 3) {
+ return;
+ }
+ }
+ va_start(ap, msg);
+ vsnprintf(buf, sizeof(buf), msg, ap);
+ va_end(ap);
+ buf[sizeof(buf) - 1] = 0;
+ for (;;) {
+ if (!l) {
+ if (_log.settings.display_file_line != 3) {
+ break;
+ }
+ } else {
+ if (l->settings.display_file_line) {
+ if (l->settings.display_file_line != 3) {
+ break;
+ }
+ } else if (l->module && l->module->display_file_line) {
+ if (l->module->display_file_line != 3) {
+ break;
+ }
+ } else if (_log.settings.display_file_line != 3) {
+ break;
+ }
+ }
+ if (l) {
+ if (l->is_obj) {
+ fprintf(stderr, "%s[%zu] %s[%p] debug: %s\n", file, line, l->name, l, buf);
+ return;
+ }
+ fprintf(stderr, "%s[%zu] %s debug: %s\n", file, line, l->name, buf);
+ return;
+ }
+ fprintf(stderr, "%s[%zu] %s debug: %s\n", file, line, _log.name, buf);
+ return;
+ }
+
+ if (l) {
+ if (l->is_obj) {
+ fprintf(stderr, "%s[%p] debug: %s\n", l->name, l, buf);
+ return;
+ }
+ fprintf(stderr, "%s debug: %s\n", l->name, buf);
+ return;
+ }
+ fprintf(stderr, "%s debug: %s\n", _log.name, buf);
+}
+
+void core_log_info(const core_log_t* l, const char* file, size_t line, const char* msg, ...)
+{
+ char buf[512];
+ va_list ap;
+ if (!l) {
+ if (_log.settings.info != 3) {
+ return;
+ }
+ } else {
+ if (l->settings.info) {
+ if (l->settings.info != 3) {
+ return;
+ }
+ } else if (l->module && l->module->info) {
+ if (l->module->info != 3) {
+ return;
+ }
+ } else if (_log.settings.info != 3) {
+ return;
+ }
+ }
+ va_start(ap, msg);
+ vsnprintf(buf, sizeof(buf), msg, ap);
+ va_end(ap);
+ buf[sizeof(buf) - 1] = 0;
+ for (;;) {
+ if (!l) {
+ if (_log.settings.display_file_line != 3) {
+ break;
+ }
+ } else {
+ if (l->settings.display_file_line) {
+ if (l->settings.display_file_line != 3) {
+ break;
+ }
+ } else if (l->module && l->module->display_file_line) {
+ if (l->module->display_file_line != 3) {
+ break;
+ }
+ } else if (_log.settings.display_file_line != 3) {
+ break;
+ }
+ }
+ if (l) {
+ if (l->is_obj) {
+ fprintf(stderr, "%s[%zu] %s[%p] info: %s\n", file, line, l->name, l, buf);
+ return;
+ }
+ fprintf(stderr, "%s[%zu] %s info: %s\n", file, line, l->name, buf);
+ return;
+ }
+ fprintf(stderr, "%s[%zu] %s info: %s\n", file, line, _log.name, buf);
+ return;
+ }
+
+ if (l) {
+ if (l->is_obj) {
+ fprintf(stderr, "%s[%p] info: %s\n", l->name, l, buf);
+ return;
+ }
+ fprintf(stderr, "%s info: %s\n", l->name, buf);
+ return;
+ }
+ fprintf(stderr, "%s info: %s\n", _log.name, buf);
+}
+
+void core_log_notice(const core_log_t* l, const char* file, size_t line, const char* msg, ...)
+{
+ char buf[512];
+ va_list ap;
+ if (!l) {
+ if (_log.settings.notice != 3) {
+ return;
+ }
+ } else {
+ if (l->settings.notice) {
+ if (l->settings.notice != 3) {
+ return;
+ }
+ } else if (l->module && l->module->notice) {
+ if (l->module->notice != 3) {
+ return;
+ }
+ } else if (_log.settings.notice != 3) {
+ return;
+ }
+ }
+ va_start(ap, msg);
+ vsnprintf(buf, sizeof(buf), msg, ap);
+ va_end(ap);
+ buf[sizeof(buf) - 1] = 0;
+ for (;;) {
+ if (!l) {
+ if (_log.settings.display_file_line != 3) {
+ break;
+ }
+ } else {
+ if (l->settings.display_file_line) {
+ if (l->settings.display_file_line != 3) {
+ break;
+ }
+ } else if (l->module && l->module->display_file_line) {
+ if (l->module->display_file_line != 3) {
+ break;
+ }
+ } else if (_log.settings.display_file_line != 3) {
+ break;
+ }
+ }
+ if (l) {
+ if (l->is_obj) {
+ fprintf(stderr, "%s[%zu] %s[%p] notice: %s\n", file, line, l->name, l, buf);
+ return;
+ }
+ fprintf(stderr, "%s[%zu] %s notice: %s\n", file, line, l->name, buf);
+ return;
+ }
+ fprintf(stderr, "%s[%zu] %s notice: %s\n", file, line, _log.name, buf);
+ return;
+ }
+
+ if (l) {
+ if (l->is_obj) {
+ fprintf(stderr, "%s[%p] notice: %s\n", l->name, l, buf);
+ return;
+ }
+ fprintf(stderr, "%s notice: %s\n", l->name, buf);
+ return;
+ }
+ fprintf(stderr, "%s notice: %s\n", _log.name, buf);
+}
+
+void core_log_warning(const core_log_t* l, const char* file, size_t line, const char* msg, ...)
+{
+ char buf[512];
+ va_list ap;
+ if (!l) {
+ if (_log.settings.warning != 3) {
+ return;
+ }
+ } else {
+ if (l->settings.warning) {
+ if (l->settings.warning != 3) {
+ return;
+ }
+ } else if (l->module && l->module->warning) {
+ if (l->module->warning != 3) {
+ return;
+ }
+ } else if (_log.settings.warning != 3) {
+ return;
+ }
+ }
+ va_start(ap, msg);
+ vsnprintf(buf, sizeof(buf), msg, ap);
+ va_end(ap);
+ buf[sizeof(buf) - 1] = 0;
+ for (;;) {
+ if (!l) {
+ if (_log.settings.display_file_line != 3) {
+ break;
+ }
+ } else {
+ if (l->settings.display_file_line) {
+ if (l->settings.display_file_line != 3) {
+ break;
+ }
+ } else if (l->module && l->module->display_file_line) {
+ if (l->module->display_file_line != 3) {
+ break;
+ }
+ } else if (_log.settings.display_file_line != 3) {
+ break;
+ }
+ }
+ if (l) {
+ if (l->is_obj) {
+ fprintf(stderr, "%s[%zu] %s[%p] warning: %s\n", file, line, l->name, l, buf);
+ return;
+ }
+ fprintf(stderr, "%s[%zu] %s warning: %s\n", file, line, l->name, buf);
+ return;
+ }
+ fprintf(stderr, "%s[%zu] %s warning: %s\n", file, line, _log.name, buf);
+ return;
+ }
+
+ if (l) {
+ if (l->is_obj) {
+ fprintf(stderr, "%s[%p] warning: %s\n", l->name, l, buf);
+ return;
+ }
+ fprintf(stderr, "%s warning: %s\n", l->name, buf);
+ return;
+ }
+ fprintf(stderr, "%s warning: %s\n", _log.name, buf);
+}
+
+void core_log_critical(const core_log_t* l, const char* file, size_t line, const char* msg, ...)
+{
+ char buf[512];
+ va_list ap;
+ va_start(ap, msg);
+ vsnprintf(buf, sizeof(buf), msg, ap);
+ va_end(ap);
+ buf[sizeof(buf) - 1] = 0;
+ for (;;) {
+ if (!l) {
+ if (_log.settings.display_file_line != 3) {
+ break;
+ }
+ } else {
+ if (l->settings.display_file_line) {
+ if (l->settings.display_file_line != 3) {
+ break;
+ }
+ } else if (l->module && l->module->display_file_line) {
+ if (l->module->display_file_line != 3) {
+ break;
+ }
+ } else if (_log.settings.display_file_line != 3) {
+ break;
+ }
+ }
+ if (l) {
+ if (l->is_obj) {
+ fprintf(stderr, "%s[%zu] %s[%p] critical: %s\n", file, line, l->name, l, buf);
+ return;
+ }
+ fprintf(stderr, "%s[%zu] %s critical: %s\n", file, line, l->name, buf);
+ return;
+ }
+ fprintf(stderr, "%s[%zu] %s critical: %s\n", file, line, _log.name, buf);
+ return;
+ }
+
+ if (l) {
+ if (l->is_obj) {
+ fprintf(stderr, "%s[%p] critical: %s\n", l->name, l, buf);
+ return;
+ }
+ fprintf(stderr, "%s critical: %s\n", l->name, buf);
+ return;
+ }
+ fprintf(stderr, "%s critical: %s\n", _log.name, buf);
+}
+
+void core_log_fatal(const core_log_t* l, const char* file, size_t line, const char* msg, ...)
+{
+ char buf[512];
+ va_list ap;
+ va_start(ap, msg);
+ vsnprintf(buf, sizeof(buf), msg, ap);
+ va_end(ap);
+ buf[sizeof(buf) - 1] = 0;
+ for (;;) {
+ if (!l) {
+ if (_log.settings.display_file_line != 3) {
+ break;
+ }
+ } else {
+ if (l->settings.display_file_line) {
+ if (l->settings.display_file_line != 3) {
+ break;
+ }
+ } else if (l->module && l->module->display_file_line) {
+ if (l->module->display_file_line != 3) {
+ break;
+ }
+ } else if (_log.settings.display_file_line != 3) {
+ break;
+ }
+ }
+ if (l) {
+ if (l->is_obj) {
+ fprintf(stderr, "%s[%zu] %s[%p] fatal: %s\n", file, line, l->name, l, buf);
+ exit(1);
+ }
+ fprintf(stderr, "%s[%zu] %s fatal: %s\n", file, line, l->name, buf);
+ exit(1);
+ }
+ fprintf(stderr, "%s[%zu] %s fatal: %s\n", file, line, _log.name, buf);
+ exit(1);
+ }
+
+ if (l) {
+ if (l->is_obj) {
+ fprintf(stderr, "%s[%p] fatal: %s\n", l->name, l, buf);
+ exit(1);
+ }
+ fprintf(stderr, "%s fatal: %s\n", l->name, buf);
+ exit(1);
+ }
+ fprintf(stderr, "%s fatal: %s\n", _log.name, buf);
+ exit(1);
+}
+
+core_log_t* core_log_log()
+{
+ return &_log;
+}
+
+#include "core/log_errstr.c"
diff --git a/include/dnsjit/core/log.h b/include/dnsjit/core/log.h
new file mode 100644
index 0000000..05b3db9
--- /dev/null
+++ b/include/dnsjit/core/log.h
@@ -0,0 +1,105 @@
+/*
+ * 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/>.
+ */
+
+#ifndef __dnsjit_core_log_h
+#define __dnsjit_core_log_h
+
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+
+#define LOG_SETTINGS_T_INIT \
+ { \
+ 0, 0, 0, 0, 0 \
+ }
+#define LOG_T_INIT(name) \
+ { \
+ name, 0, LOG_SETTINGS_T_INIT, 0 \
+ }
+#define LOG_T_INIT_OBJ(name) \
+ { \
+ name, 1, LOG_SETTINGS_T_INIT, &_log.settings \
+ }
+
+#include <dnsjit/core/log.hh>
+
+#ifdef DNSJIT_NO_LOGGING
+#define ldebug(msg...)
+#define linfo(msg...)
+#define lnotice(msg...)
+#define lwarning(msg...)
+#define lcritical(msg...)
+#define lpdebug(msg...)
+#define lpinfo(msg...)
+#define lpnotice(msg...)
+#define lpwarning(msg...)
+#define lpcritical(msg...)
+#define mldebug(msg...)
+#define mlinfo(msg...)
+#define mlnotice(msg...)
+#define mlwarning(msg...)
+#define mlcritical(msg...)
+#define gldebug(msg...)
+#define glinfo(msg...)
+#define glnotice(msg...)
+#define glwarning(msg...)
+#define glcritical(msg...)
+#else
+#define ldebug(msg...) core_log_debug(&self->_log, __FILE__, __LINE__, msg)
+#define linfo(msg...) core_log_info(&self->_log, __FILE__, __LINE__, msg)
+#define lnotice(msg...) core_log_notice(&self->_log, __FILE__, __LINE__, msg)
+#define lwarning(msg...) core_log_warning(&self->_log, __FILE__, __LINE__, msg)
+#define lcritical(msg...) core_log_critical(&self->_log, __FILE__, __LINE__, msg)
+#define lpdebug(msg...) core_log_debug(self->_log, __FILE__, __LINE__, msg)
+#define lpinfo(msg...) core_log_info(self->_log, __FILE__, __LINE__, msg)
+#define lpnotice(msg...) core_log_notice(self->_log, __FILE__, __LINE__, msg)
+#define lpwarning(msg...) core_log_warning(self->_log, __FILE__, __LINE__, msg)
+#define lpcritical(msg...) core_log_critical(self->_log, __FILE__, __LINE__, msg)
+#define mldebug(msg...) core_log_debug(&_log, __FILE__, __LINE__, msg)
+#define mlinfo(msg...) core_log_info(&_log, __FILE__, __LINE__, msg)
+#define mlnotice(msg...) core_log_notice(&_log, __FILE__, __LINE__, msg)
+#define mlwarning(msg...) core_log_warning(&_log, __FILE__, __LINE__, msg)
+#define mlcritical(msg...) core_log_critical(&_log, __FILE__, __LINE__, msg)
+#define gldebug(msg...) core_log_debug(0, __FILE__, __LINE__, msg)
+#define glinfo(msg...) core_log_info(0, __FILE__, __LINE__, msg)
+#define glnotice(msg...) core_log_notice(0, __FILE__, __LINE__, msg)
+#define glwarning(msg...) core_log_warning(0, __FILE__, __LINE__, msg)
+#define glcritical(msg...) core_log_critical(0, __FILE__, __LINE__, msg)
+#endif
+
+#define lfatal(msg...) core_log_fatal(&self->_log, __FILE__, __LINE__, msg)
+#define lpfatal(msg...) core_log_fatal(self->_log, __FILE__, __LINE__, msg)
+#define mlfatal(msg...) core_log_fatal(&_log, __FILE__, __LINE__, msg)
+#define glfatal(msg...) core_log_fatal(0, __FILE__, __LINE__, msg)
+
+#define lfatal_oom(expression) \
+ if (!(expression)) \
+ core_log_fatal(&self->_log, __FILE__, __LINE__, "out of memory")
+#define lpfatal_oom(expression) \
+ if (!(expression)) \
+ core_log_fatal(self->_log, __FILE__, __LINE__, "out of memory")
+#define mlfatal_oom(expression) \
+ if (!(expression)) \
+ core_log_fatal(&_log, __FILE__, __LINE__, "out of memory")
+#define glfatal_oom(expression) \
+ if (!(expression)) \
+ core_log_fatal(0, __FILE__, __LINE__, "out of memory")
+
+#endif
diff --git a/include/dnsjit/core/log.hh b/include/dnsjit/core/log.hh
new file mode 100644
index 0000000..7273879
--- /dev/null
+++ b/include/dnsjit/core/log.hh
@@ -0,0 +1,44 @@
+/*
+ * 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/>.
+ */
+
+typedef struct core_log_settings {
+ uint8_t debug;
+ uint8_t info;
+ uint8_t notice;
+ uint8_t warning;
+ uint8_t display_file_line;
+} core_log_settings_t;
+
+typedef struct core_log {
+ char name[32];
+ uint8_t is_obj;
+ core_log_settings_t settings;
+ const core_log_settings_t* module;
+} core_log_t;
+
+void core_log_debug(const core_log_t* l, const char* file, size_t line, const char* msg, ...);
+void core_log_info(const core_log_t* l, const char* file, size_t line, const char* msg, ...);
+void core_log_notice(const core_log_t* l, const char* file, size_t line, const char* msg, ...);
+void core_log_warning(const core_log_t* l, const char* file, size_t line, const char* msg, ...);
+void core_log_critical(const core_log_t* l, const char* file, size_t line, const char* msg, ...);
+void core_log_fatal(const core_log_t* l, const char* file, size_t line, const char* msg, ...);
+const char* core_log_errstr(int err);
+
+core_log_t* core_log_log();
diff --git a/include/dnsjit/core/log.lua b/include/dnsjit/core/log.lua
new file mode 100644
index 0000000..d62cbbc
--- /dev/null
+++ b/include/dnsjit/core/log.lua
@@ -0,0 +1,639 @@
+-- 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.core.log
+-- Core logging facility
+-- .SS Usage to control global log level
+-- local log = require("dnsjit.core.log")
+-- log.enable("all")
+-- log.disable("debug")
+-- .SS Usage to control module log level
+-- local example = require("example") -- Example as below
+-- example.log():enable("all")
+-- example.log():disable("debug")
+-- .SS Usage to control object instance log level
+-- local example = require("example") -- Example as below
+-- local obj = example.new()
+-- obj:log():enable("all")
+-- obj:log():disable("debug")
+-- .SS Usage in C module
+-- .B NOTE
+-- naming of variables and module only globals are required to exactly as
+-- described in order for the macros to work;
+-- .B self
+-- is the pointer to the object instance,
+-- .B self->_log
+-- is the object instance logging configuration struct,
+-- .B _log
+-- is the module logging configuration struct.
+-- .LP
+-- Include logging:
+-- #include "core/log.h"
+-- .LP
+-- Add the logging struct to the module struct:
+-- typedef struct example {
+-- core_log_t _log;
+-- ...
+-- } example_t;
+-- .LP
+-- Add a module logging configuration and a struct default:
+-- static core_log_t _log = LOG_T_INIT("example");
+-- static example_t _defaults = {
+-- LOG_T_INIT_OBJ("example"),
+-- ...
+-- };
+-- .LP
+-- Use new/free and/or init/destroy functions (depends if you create the
+-- object in Lua or not):
+-- example_t* example_new() {
+-- example_t* self = calloc(1, sizeof(example_t));
+-- .
+-- *self = _defaults;
+-- ldebug("new()");
+-- .
+-- return self;
+-- }
+-- .
+-- void example_free(example_t* self) {
+-- ldebug("free()");
+-- free(self);
+-- }
+-- .
+-- int example_init(example_t* self) {
+-- *self = _defaults;
+-- .
+-- ldebug("init()");
+-- .
+-- return 0;
+-- }
+-- .
+-- void example_destroy(example_t* self) {
+-- ldebug("destroy()");
+-- ...
+-- }
+-- .LP
+-- In the Lua part of the C module you need to create a function that
+-- returns either the object instance Log or the modules Log.
+-- .LP
+-- Add C function to get module only Log:
+-- core_log_t* example_log() {
+-- return &_log;
+-- }
+-- .LP
+-- For the structures metatable add the following function:
+-- local ffi = require("ffi")
+-- local C = ffi.C
+-- .
+-- function Example:log()
+-- if self == nil then
+-- return C.example_log()
+-- end
+-- return self._log
+-- end
+-- .SS Usage in pure Lua module
+-- local log = require("dnsjit.core.log")
+-- local ffi = require("ffi")
+-- local C = ffi.C
+-- .
+-- local Example = {}
+-- local module_log = log.new("example")
+-- .
+-- function Example.new()
+-- local self = setmetatable({
+-- _log = log.new("example", module_log),
+-- }, { __index = Example })
+-- .
+-- self._log:debug("new()")
+-- .
+-- return self
+-- end
+-- .
+-- function Example:log()
+-- if self == nil then
+-- return module_log
+-- end
+-- return self._log
+-- end
+--
+-- Core logging facility used by all modules.
+-- .SS Log levels
+-- .TP
+-- all
+-- Keyword to enable/disable all changeable log levels.
+-- .TP
+-- debug
+-- Used for debug information.
+-- .TP
+-- info
+-- Used for informational processing messages.
+-- .TP
+-- notice
+-- Used for messages of that may have impact on processing.
+-- .TP
+-- warning
+-- Used for messages that has impact on processing.
+-- .TP
+-- critical
+-- Used for messages that have severe impact on processing, this level can
+-- not be disabled.
+-- .TP
+-- fatal
+-- Used to display a message before stopping all processing and existing,
+-- this level can not be disabled.
+-- .SS C macros
+-- .TP
+-- Object instance macros
+-- The following macros uses
+-- .IR &self->_log :
+-- .BR ldebug(msg...) ,
+-- .BR linfo(msg...) ,
+-- .BR lnotice(msg...) ,
+-- .BR lwarning(msg...) ,
+-- .BR lcritical(msg...) ,
+-- .BR lfatal(msg...) .
+-- .TP
+-- Object pointer instance macros
+-- The following macros uses
+-- .IR self->_log :
+-- .BR lpdebug(msg...) ,
+-- .BR lpinfo(msg...) ,
+-- .BR lpnotice(msg...) ,
+-- .BR lpwarning(msg...) ,
+-- .BR lpcritical(msg...) ,
+-- .BR lpfatal(msg...) .
+-- .TP
+-- Module macros
+-- The following macros uses
+-- .IR &_log :
+-- .BR mldebug(msg...) ,
+-- .BR mlinfo(msg...) ,
+-- .BR mlnotice(msg...) ,
+-- .BR mlwarning(msg...) ,
+-- .BR mlcritical(msg...) ,
+-- .BR mlfatal(msg...) .
+-- .TP
+-- Global macros
+-- The following macros uses the global logging configuration:
+-- .BR gldebug(msg...) ,
+-- .BR glinfo(msg...) ,
+-- .BR glnotice(msg...) ,
+-- .BR glwarning(msg...) ,
+-- .BR glcritical(msg...) ,
+-- .BR glfatal(msg...) .
+module(...,package.seeall)
+
+require("dnsjit.core.log_h")
+local ffi = require("ffi")
+local C = ffi.C
+local L = C.core_log_log()
+
+local t_name = "core_log_t"
+local core_log_t
+local Log = {}
+
+-- Create a new Log object with the given module
+-- .I name
+-- and an optional shared
+-- .I module
+-- Log object.
+function Log.new(name, module)
+ local self
+ if ffi.istype(t_name, module) then
+ self = core_log_t({ is_obj = 1, module = module.settings })
+ else
+ self = core_log_t()
+ end
+
+ local len = #name
+ if len > 31 then
+ len = 31
+ end
+ ffi.copy(self.name, name, len)
+ self.name[len] = 0
+
+ return self
+end
+
+-- Enable specified log level.
+function Log:enable(level)
+ if not ffi.istype(t_name, self) then
+ level = self
+ self = L
+ end
+ if level == "all" then
+ self.settings.debug = 3
+ self.settings.info = 3
+ self.settings.notice = 3
+ self.settings.warning = 3
+ elseif level == "debug" then
+ self.settings.debug = 3
+ elseif level == "info" then
+ self.settings.info = 3
+ elseif level == "notice" then
+ self.settings.notice = 3
+ elseif level == "warning" then
+ self.settings.warning = 3
+ else
+ error("invalid log level: "..level)
+ end
+end
+
+-- Disable specified log level.
+function Log:disable(level)
+ if not ffi.istype(t_name, self) then
+ level = self
+ self = L
+ end
+ if level == "all" then
+ self.settings.debug = 2
+ self.settings.info = 2
+ self.settings.notice = 2
+ self.settings.warning = 2
+ elseif level == "debug" then
+ self.settings.debug = 2
+ elseif level == "info" then
+ self.settings.info = 2
+ elseif level == "notice" then
+ self.settings.notice = 2
+ elseif level == "warning" then
+ self.settings.warning = 2
+ else
+ error("invalid log level: "..level)
+ end
+end
+
+-- Clear specified log level, which means it will revert back to default
+-- or inherited settings.
+function Log:clear(level)
+ if not ffi.istype(t_name, self) then
+ level = self
+ self = L
+ end
+ if level == "all" then
+ self.settings.debug = 0
+ self.settings.info = 0
+ self.settings.notice = 0
+ self.settings.warning = 0
+ elseif level == "debug" then
+ self.settings.debug = 0
+ elseif level == "info" then
+ self.settings.info = 0
+ elseif level == "notice" then
+ self.settings.notice = 0
+ elseif level == "warning" then
+ self.settings.warning = 0
+ else
+ error("invalid log level: "..level)
+ end
+end
+
+-- Enable or disable the displaying of file and line for messages.
+function Log:display_file_line(bool)
+ if not ffi.istype(t_name, self) then
+ bool = self
+ self = L
+ end
+ if bool == true then
+ self.settings.display_file_line = 3
+ else
+ self.settings.display_file_line = 0
+ end
+end
+
+-- Convert error number to its text representation.
+function Log.errstr(errno)
+ return ffi.string(C.core_log_errstr(errno))
+end
+
+-- Generate a debug message.
+function Log.debug(self, ...)
+ local format
+ if not ffi.istype(t_name, self) then
+ format = self
+ self = nil
+ end
+ if not self then
+ if L.settings.debug ~= 3 then
+ return
+ end
+ else
+ if self.settings.debug ~= 0 then
+ if self.settings.debug ~= 3 then
+ return
+ end
+ elseif self.module ~= nil and self.module.debug ~= 0 then
+ if self.module.debug ~= 3 then
+ return
+ end
+ elseif L.settings.debug ~= 3 then
+ return
+ end
+ end
+ while true do
+ if not self then
+ if L.settings.display_file_line ~= 3 then
+ break
+ end
+ else
+ if self.settings.display_file_line ~= 0 then
+ if self.settings.display_file_line ~= 3 then
+ break
+ end
+ elseif self.module ~= nil and self.module.display_file_line ~= 0 then
+ if self.module.display_file_line ~= 3 then
+ break
+ end
+ elseif L.settings.display_file_line ~= 3 then
+ break
+ end
+ end
+ local info = debug.getinfo(2, "S")
+ if format then
+ C.core_log_debug(self, info.source, info.linedefined, format, ...)
+ return
+ end
+ C.core_log_debug(self, info.source, info.linedefined, ...)
+ return
+ end
+
+ if format then
+ C.core_log_debug(self, nil, 0, format, ...)
+ return
+ end
+ C.core_log_debug(self, nil, 0, ...)
+end
+
+-- Generate an info message.
+function Log.info(self, ...)
+ local format
+ if not ffi.istype(t_name, self) then
+ format = self
+ self = nil
+ end
+ if not self then
+ if L.settings.info ~= 3 then
+ return
+ end
+ else
+ if self.settings.info ~= 0 then
+ if self.settings.info ~= 3 then
+ return
+ end
+ elseif self.module ~= nil and self.module.info ~= 0 then
+ if self.module.info ~= 3 then
+ return
+ end
+ elseif L.settings.info ~= 3 then
+ return
+ end
+ end
+ while true do
+ if not self then
+ if L.settings.display_file_line ~= 3 then
+ break
+ end
+ else
+ if self.settings.display_file_line ~= 0 then
+ if self.settings.display_file_line ~= 3 then
+ break
+ end
+ elseif self.module ~= nil and self.module.display_file_line ~= 0 then
+ if self.module.display_file_line ~= 3 then
+ break
+ end
+ elseif L.settings.display_file_line ~= 3 then
+ break
+ end
+ end
+ local info = debug.getinfo(2, "S")
+ if format then
+ C.core_log_info(self, info.source, info.linedefined, format, ...)
+ return
+ end
+ C.core_log_info(self, info.source, info.linedefined, ...)
+ return
+ end
+
+ if format then
+ C.core_log_info(self, nil, 0, format, ...)
+ return
+ end
+ C.core_log_info(self, nil, 0, ...)
+end
+
+-- Generate a notice message.
+function Log.notice(self, ...)
+ local format
+ if not ffi.istype(t_name, self) then
+ format = self
+ self = nil
+ end
+ if not self then
+ if L.settings.notice ~= 3 then
+ return
+ end
+ else
+ if self.settings.notice ~= 0 then
+ if self.settings.notice ~= 3 then
+ return
+ end
+ elseif self.module ~= nil and self.module.notice ~= 0 then
+ if self.module.notice ~= 3 then
+ return
+ end
+ elseif L.settings.notice ~= 3 then
+ return
+ end
+ end
+ while true do
+ if not self then
+ if L.settings.display_file_line ~= 3 then
+ break
+ end
+ else
+ if self.settings.display_file_line ~= 0 then
+ if self.settings.display_file_line ~= 3 then
+ break
+ end
+ elseif self.module ~= nil and self.module.display_file_line ~= 0 then
+ if self.module.display_file_line ~= 3 then
+ break
+ end
+ elseif L.settings.display_file_line ~= 3 then
+ break
+ end
+ end
+ local info = debug.getinfo(2, "S")
+ if format then
+ C.core_log_notice(self, info.source, info.linedefined, format, ...)
+ return
+ end
+ C.core_log_notice(self, info.source, info.linedefined, ...)
+ return
+ end
+
+ if format then
+ C.core_log_notice(self, nil, 0, format, ...)
+ return
+ end
+ C.core_log_notice(self, nil, 0, ...)
+end
+
+-- Generate a warning message.
+function Log.warning(self, ...)
+ local format
+ if not ffi.istype(t_name, self) then
+ format = self
+ self = nil
+ end
+ if not self then
+ if L.settings.warning ~= 3 then
+ return
+ end
+ else
+ if self.settings.warning ~= 0 then
+ if self.settings.warning ~= 3 then
+ return
+ end
+ elseif self.module ~= nil and self.module.warning ~= 0 then
+ if self.module.warning ~= 3 then
+ return
+ end
+ elseif L.settings.warning ~= 3 then
+ return
+ end
+ end
+ while true do
+ if not self then
+ if L.settings.display_file_line ~= 3 then
+ break
+ end
+ else
+ if self.settings.display_file_line ~= 0 then
+ if self.settings.display_file_line ~= 3 then
+ break
+ end
+ elseif self.module ~= nil and self.module.display_file_line ~= 0 then
+ if self.module.display_file_line ~= 3 then
+ break
+ end
+ elseif L.settings.display_file_line ~= 3 then
+ break
+ end
+ end
+ local info = debug.getinfo(2, "S")
+ if format then
+ C.core_log_warning(self, info.source, info.linedefined, format, ...)
+ return
+ end
+ C.core_log_warning(self, info.source, info.linedefined, ...)
+ return
+ end
+
+ if format then
+ C.core_log_warning(self, nil, 0, format, ...)
+ return
+ end
+ C.core_log_warning(self, nil, 0, ...)
+end
+
+-- Generate a critical message.
+function Log.critical(self, ...)
+ local format
+ if not ffi.istype(t_name, self) then
+ format = self
+ self = nil
+ end
+ while true do
+ if not self then
+ if L.settings.display_file_line ~= 3 then
+ break
+ end
+ else
+ if self.settings.display_file_line ~= 0 then
+ if self.settings.display_file_line ~= 3 then
+ break
+ end
+ elseif self.module ~= nil and self.module.display_file_line ~= 0 then
+ if self.module.display_file_line ~= 3 then
+ break
+ end
+ elseif L.settings.display_file_line ~= 3 then
+ break
+ end
+ end
+ local info = debug.getinfo(2, "S")
+ if format then
+ C.core_log_critical(self, info.source, info.linedefined, format, ...)
+ return
+ end
+ C.core_log_critical(self, info.source, info.linedefined, ...)
+ return
+ end
+
+ if format then
+ C.core_log_critical(self, nil, 0, format, ...)
+ return
+ end
+ C.core_log_critical(self, nil, 0, ...)
+end
+
+-- Generate a fatal message.
+function Log.fatal(self, ...)
+ local format
+ if not ffi.istype(t_name, self) then
+ format = self
+ self = nil
+ end
+ while true do
+ if not self then
+ if L.settings.display_file_line ~= 3 then
+ break
+ end
+ else
+ if self.settings.display_file_line ~= 0 then
+ if self.settings.display_file_line ~= 3 then
+ break
+ end
+ elseif self.module ~= nil and self.module.display_file_line ~= 0 then
+ if self.module.display_file_line ~= 3 then
+ break
+ end
+ elseif L.settings.display_file_line ~= 3 then
+ break
+ end
+ end
+ local info = debug.getinfo(2, "S")
+ if format then
+ C.core_log_fatal(self, info.source, info.linedefined, format, ...)
+ return
+ end
+ C.core_log_fatal(self, info.source, info.linedefined, ...)
+ return
+ end
+
+ if format then
+ C.core_log_fatal(self, nil, 0, format, ...)
+ return
+ end
+ C.core_log_fatal(self, nil, 0, ...)
+end
+
+core_log_t = ffi.metatype(t_name, { __index = Log })
+
+return Log
diff --git a/include/dnsjit/core/object.c b/include/dnsjit/core/object.c
new file mode 100644
index 0000000..8356f25
--- /dev/null
+++ b/include/dnsjit/core/object.c
@@ -0,0 +1,135 @@
+/*
+ * 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 "core/object.h"
+#include "core/assert.h"
+#include "core/object/pcap.h"
+#include "core/object/ether.h"
+#include "core/object/null.h"
+#include "core/object/loop.h"
+#include "core/object/linuxsll.h"
+#include "core/object/ieee802.h"
+#include "core/object/gre.h"
+#include "core/object/ip.h"
+#include "core/object/ip6.h"
+#include "core/object/icmp.h"
+#include "core/object/icmp6.h"
+#include "core/object/udp.h"
+#include "core/object/tcp.h"
+#include "core/object/payload.h"
+#include "core/object/dns.h"
+
+core_object_t* core_object_copy(const core_object_t* self)
+{
+ glassert_self();
+
+ switch (self->obj_type) {
+ case CORE_OBJECT_PCAP:
+ return (core_object_t*)core_object_pcap_copy((core_object_pcap_t*)self);
+ case CORE_OBJECT_ETHER:
+ return (core_object_t*)core_object_ether_copy((core_object_ether_t*)self);
+ case CORE_OBJECT_NULL:
+ return (core_object_t*)core_object_null_copy((core_object_null_t*)self);
+ case CORE_OBJECT_LOOP:
+ return (core_object_t*)core_object_loop_copy((core_object_loop_t*)self);
+ case CORE_OBJECT_LINUXSLL:
+ return (core_object_t*)core_object_linuxsll_copy((core_object_linuxsll_t*)self);
+ case CORE_OBJECT_IEEE802:
+ return (core_object_t*)core_object_ieee802_copy((core_object_ieee802_t*)self);
+ case CORE_OBJECT_GRE:
+ return (core_object_t*)core_object_gre_copy((core_object_gre_t*)self);
+ case CORE_OBJECT_IP:
+ return (core_object_t*)core_object_ip_copy((core_object_ip_t*)self);
+ case CORE_OBJECT_IP6:
+ return (core_object_t*)core_object_ip6_copy((core_object_ip6_t*)self);
+ case CORE_OBJECT_ICMP:
+ return (core_object_t*)core_object_icmp_copy((core_object_icmp_t*)self);
+ case CORE_OBJECT_ICMP6:
+ return (core_object_t*)core_object_icmp6_copy((core_object_icmp6_t*)self);
+ case CORE_OBJECT_UDP:
+ return (core_object_t*)core_object_udp_copy((core_object_udp_t*)self);
+ case CORE_OBJECT_TCP:
+ return (core_object_t*)core_object_tcp_copy((core_object_tcp_t*)self);
+ case CORE_OBJECT_PAYLOAD:
+ return (core_object_t*)core_object_payload_copy((core_object_payload_t*)self);
+ case CORE_OBJECT_DNS:
+ return (core_object_t*)core_object_dns_copy((core_object_dns_t*)self);
+ default:
+ glfatal("unknown type %d", self->obj_type);
+ }
+ return 0;
+}
+
+void core_object_free(core_object_t* self)
+{
+ glassert_self();
+
+ switch (self->obj_type) {
+ case CORE_OBJECT_PCAP:
+ core_object_pcap_free((core_object_pcap_t*)self);
+ break;
+ case CORE_OBJECT_ETHER:
+ core_object_ether_free((core_object_ether_t*)self);
+ break;
+ case CORE_OBJECT_NULL:
+ core_object_null_free((core_object_null_t*)self);
+ break;
+ case CORE_OBJECT_LOOP:
+ core_object_loop_free((core_object_loop_t*)self);
+ break;
+ case CORE_OBJECT_LINUXSLL:
+ core_object_linuxsll_free((core_object_linuxsll_t*)self);
+ break;
+ case CORE_OBJECT_IEEE802:
+ core_object_ieee802_free((core_object_ieee802_t*)self);
+ break;
+ case CORE_OBJECT_GRE:
+ core_object_gre_free((core_object_gre_t*)self);
+ break;
+ case CORE_OBJECT_IP:
+ core_object_ip_free((core_object_ip_t*)self);
+ break;
+ case CORE_OBJECT_IP6:
+ core_object_ip6_free((core_object_ip6_t*)self);
+ break;
+ case CORE_OBJECT_ICMP:
+ core_object_icmp_free((core_object_icmp_t*)self);
+ break;
+ case CORE_OBJECT_ICMP6:
+ core_object_icmp6_free((core_object_icmp6_t*)self);
+ break;
+ case CORE_OBJECT_UDP:
+ core_object_udp_free((core_object_udp_t*)self);
+ break;
+ case CORE_OBJECT_TCP:
+ core_object_tcp_free((core_object_tcp_t*)self);
+ break;
+ case CORE_OBJECT_PAYLOAD:
+ core_object_payload_free((core_object_payload_t*)self);
+ break;
+ case CORE_OBJECT_DNS:
+ core_object_dns_free((core_object_dns_t*)self);
+ break;
+ default:
+ glfatal("unknown type %d", self->obj_type);
+ }
+}
diff --git a/include/dnsjit/core/object.h b/include/dnsjit/core/object.h
new file mode 100644
index 0000000..8729cde
--- /dev/null
+++ b/include/dnsjit/core/object.h
@@ -0,0 +1,51 @@
+/*
+ * 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/>.
+ */
+
+#ifndef __dnsjit_core_object_h
+#define __dnsjit_core_object_h
+
+#define CORE_OBJECT_NONE 0
+#define CORE_OBJECT_PCAP 1
+/* link level objects */
+#define CORE_OBJECT_ETHER 10
+#define CORE_OBJECT_NULL 11
+#define CORE_OBJECT_LOOP 12
+#define CORE_OBJECT_LINUXSLL 13
+#define CORE_OBJECT_IEEE802 14
+#define CORE_OBJECT_GRE 15
+/* protocol objects */
+#define CORE_OBJECT_IP 20
+#define CORE_OBJECT_IP6 21
+#define CORE_OBJECT_ICMP 22
+#define CORE_OBJECT_ICMP6 23
+/* payload carrying objects */
+#define CORE_OBJECT_UDP 30
+#define CORE_OBJECT_TCP 31
+/* payload */
+#define CORE_OBJECT_PAYLOAD 40
+/* service object(s) */
+#define CORE_OBJECT_DNS 50
+
+#include <stdint.h>
+#include <dnsjit/core/object.hh>
+
+#define CORE_OBJECT_INIT(type, prev) (core_object_t*)prev, type
+
+#endif
diff --git a/include/dnsjit/core/object.hh b/include/dnsjit/core/object.hh
new file mode 100644
index 0000000..b19f858
--- /dev/null
+++ b/include/dnsjit/core/object.hh
@@ -0,0 +1,28 @@
+/*
+ * 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 objectd a copy of the GNU General Public License
+ * along with dnsjit. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+typedef struct core_object core_object_t;
+struct core_object {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+};
+
+core_object_t* core_object_copy(const core_object_t* self);
+void core_object_free(core_object_t* self);
diff --git a/include/dnsjit/core/object.lua b/include/dnsjit/core/object.lua
new file mode 100644
index 0000000..7f58829
--- /dev/null
+++ b/include/dnsjit/core/object.lua
@@ -0,0 +1,177 @@
+-- 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.core.object
+-- Base object that is passed between receiver and receivee
+-- require("dnsjit.core.object")
+-- print(object:type())
+-- packet = object:cast()
+--
+-- This is the base object that can be casted to other objects that to
+-- describe a DNS message, how it was captured or generated.
+-- Objects can be chained together, for example a DNS message is created
+-- ontop of a packet.
+-- .SS Attributes
+-- .TP
+-- obj_type
+-- The enum of the object type.
+-- .TP
+-- obj_prev
+-- The previous object in the object chain.
+module(...,package.seeall)
+
+require("dnsjit.core.object_h")
+require("dnsjit.core.object.pcap_h")
+require("dnsjit.core.object.ether_h")
+require("dnsjit.core.object.null_h")
+require("dnsjit.core.object.loop_h")
+require("dnsjit.core.object.linuxsll_h")
+require("dnsjit.core.object.ieee802_h")
+require("dnsjit.core.object.gre_h")
+require("dnsjit.core.object.ip_h")
+require("dnsjit.core.object.ip6_h")
+require("dnsjit.core.object.icmp_h")
+require("dnsjit.core.object.icmp6_h")
+require("dnsjit.core.object.udp_h")
+require("dnsjit.core.object.tcp_h")
+require("dnsjit.core.object.payload_h")
+require("dnsjit.core.object.dns_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_t"
+local core_object_t
+local Object = {
+ NONE = 0,
+ PCAP = 1,
+ ETHER = 10,
+ NULL = 11,
+ LOOP = 12,
+ LINUXSLL = 13,
+ IEEE802 = 14,
+ GRE = 15,
+ IP = 20,
+ IP6 = 21,
+ ICMP = 22,
+ ICMP6 = 23,
+ UDP = 30,
+ TCP = 31,
+ PAYLOAD = 40,
+ DNS = 50
+}
+
+local _type = {}
+_type[Object.PCAP] = "pcap"
+_type[Object.ETHER] = "ether"
+_type[Object.NULL] = "null"
+_type[Object.LOOP] = "loop"
+_type[Object.LINUXSLL] = "linuxsll"
+_type[Object.IEEE802] = "ieee802"
+_type[Object.GRE] = "gre"
+_type[Object.IP] = "ip"
+_type[Object.IP6] = "ip6"
+_type[Object.ICMP] = "icmp"
+_type[Object.ICMP6] = "icmp6"
+_type[Object.UDP] = "udp"
+_type[Object.TCP] = "tcp"
+_type[Object.PAYLOAD] = "payload"
+_type[Object.DNS] = "dns"
+
+_type[Object.NONE] = "none"
+
+-- Return the textual type of the object.
+function Object:type()
+ return _type[self.obj_type]
+end
+
+-- Return the previous object.
+function Object:prev()
+ return self.obj_prev
+end
+
+local _cast = {}
+_cast[Object.PCAP] = "core_object_pcap_t*"
+_cast[Object.ETHER] = "core_object_ether_t*"
+_cast[Object.NULL] = "core_object_null_t*"
+_cast[Object.LOOP] = "core_object_loop_t*"
+_cast[Object.LINUXSLL] = "core_object_linuxsll_t*"
+_cast[Object.IEEE802] = "core_object_ieee802_t*"
+_cast[Object.GRE] = "core_object_gre_t*"
+_cast[Object.IP] = "core_object_ip_t*"
+_cast[Object.IP6] = "core_object_ip6_t*"
+_cast[Object.ICMP] = "core_object_icmp_t*"
+_cast[Object.ICMP6] = "core_object_icmp6_t*"
+_cast[Object.UDP] = "core_object_udp_t*"
+_cast[Object.TCP] = "core_object_tcp_t*"
+_cast[Object.PAYLOAD] = "core_object_payload_t*"
+_cast[Object.DNS] = "core_object_dns_t*"
+
+-- Cast the object to the underlining object module and return it.
+function Object:cast()
+ return ffi.cast(_cast[self.obj_type], self)
+end
+
+-- Cast the object to the specified object module and return it.
+-- Returns nil if the object chain doesn't contained the specified object type.
+function Object:cast_to(obj_type)
+ if obj_type == nil then
+ obj_type = self.obj_type
+ end
+
+ local obj = self
+ while obj.obj_type ~= obj_type do
+ obj = obj.obj_prev
+ if obj == nil then return nil end
+ end
+
+ return ffi.cast(_cast[obj_type], obj)
+end
+
+-- Cast the object to the generic object module and return it.
+function Object:uncast()
+ return self
+end
+
+-- Make a copy of the object and return it.
+function Object:copy()
+ return C.core_object_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Object:free()
+ C.core_object_free(self)
+end
+
+core_object_t = ffi.metatype(t_name, { __index = Object })
+
+-- dnsjit.core.object.pcap (3),
+-- dnsjit.core.object.ether (3),
+-- dnsjit.core.object.null (3),
+-- dnsjit.core.object.loop (3),
+-- dnsjit.core.object.linuxsll (3),
+-- dnsjit.core.object.ieee802 (3),
+-- dnsjit.core.object.gre (3),
+-- dnsjit.core.object.ip (3),
+-- dnsjit.core.object.ip6 (3),
+-- dnsjit.core.object.icmp (3),
+-- dnsjit.core.object.icmp6 (3),
+-- dnsjit.core.object.udp (3),
+-- dnsjit.core.object.tcp (3),
+-- dnsjit.core.object.payload (3),
+-- dnsjit.core.object.dns (3)
+return Object
diff --git a/include/dnsjit/core/object/dns.c b/include/dnsjit/core/object/dns.c
new file mode 100644
index 0000000..49aec6d
--- /dev/null
+++ b/include/dnsjit/core/object/dns.c
@@ -0,0 +1,471 @@
+/*
+ * 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 "core/object/dns.h"
+#include "core/object/payload.h"
+#include "core/assert.h"
+
+#include <stdlib.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
+
+#define _ERR_MALFORMED -2
+#define _ERR_NEEDLABELS -3
+
+static core_log_t _log = LOG_T_INIT("core.object.dns");
+static core_object_dns_t _defaults = CORE_OBJECT_DNS_INIT(0);
+
+static core_object_dns_label_t _defaults_label = { 0 };
+static core_object_dns_rr_t _defaults_rr = { 0 };
+static core_object_dns_q_t _defaults_q = { 0 };
+
+core_log_t* core_object_dns_log()
+{
+ return &_log;
+}
+
+core_object_dns_t* core_object_dns_new()
+{
+ core_object_dns_t* self;
+
+ mlfatal_oom(self = malloc(sizeof(core_object_dns_t)));
+ *self = _defaults;
+
+ return self;
+}
+
+core_object_dns_t* core_object_dns_copy(const core_object_dns_t* self)
+{
+ core_object_dns_t* copy;
+ mlassert_self();
+
+ mlfatal_oom(copy = malloc(sizeof(core_object_dns_t)));
+ memcpy(copy, self, sizeof(core_object_dns_t));
+ copy->obj_prev = 0;
+
+ return (core_object_dns_t*)copy;
+}
+
+void core_object_dns_free(core_object_dns_t* self)
+{
+ mlassert_self();
+ free(self);
+}
+
+#define need8(v, p, l) \
+ if (l < 1) { \
+ break; \
+ } \
+ v = *p; \
+ p += 1; \
+ l -= 1
+
+static inline uint16_t _need16(const void* ptr)
+{
+ uint16_t v;
+ memcpy(&v, ptr, sizeof(v));
+ return be16toh(v);
+}
+
+#define need16(v, p, l) \
+ if (l < 2) { \
+ break; \
+ } \
+ v = _need16(p); \
+ p += 2; \
+ l -= 2
+
+static inline uint32_t _need32(const void* ptr)
+{
+ uint32_t v;
+ memcpy(&v, ptr, sizeof(v));
+ return be32toh(v);
+}
+
+#define need32(v, p, l) \
+ if (l < 4) { \
+ break; \
+ } \
+ v = _need32(p); \
+ p += 4; \
+ l -= 4
+
+#define needxb(b, x, p, l) \
+ if (l < x) { \
+ break; \
+ } \
+ memcpy(b, p, x); \
+ p += x; \
+ l -= x
+
+#define advancexb(x, p, l) \
+ if (l < x) { \
+ break; \
+ } \
+ p += x; \
+ l -= x
+
+int core_object_dns_parse_header(core_object_dns_t* self)
+{
+ const core_object_payload_t* payload;
+ uint8_t byte;
+ mlassert_self();
+
+ if (!(payload = (core_object_payload_t*)self->obj_prev) || payload->obj_type != CORE_OBJECT_PAYLOAD) {
+ mlfatal("no obj_prev or invalid type");
+ }
+ if (!payload->payload || !payload->len) {
+ mlfatal("no payload set or zero length");
+ }
+
+ self->payload = self->at = payload->payload;
+ self->len = self->left = payload->len;
+
+ for (;;) {
+ if (self->includes_dnslen) {
+ need16(self->dnslen, self->at, self->left);
+ self->have_dnslen = 1;
+ }
+ need16(self->id, self->at, self->left);
+ self->have_id = 1;
+
+ need8(byte, self->at, self->left);
+ self->qr = byte & (1 << 7) ? 1 : 0;
+ self->opcode = (byte >> 3) & 0xf;
+ self->aa = byte & (1 << 2) ? 1 : 0;
+ self->tc = byte & (1 << 1) ? 1 : 0;
+ self->rd = byte & (1 << 0) ? 1 : 0;
+ self->have_qr = self->have_opcode = self->have_aa = self->have_tc = self->have_rd = 1;
+
+ need8(byte, self->at, self->left);
+ self->ra = byte & (1 << 7) ? 1 : 0;
+ self->z = byte & (1 << 6) ? 1 : 0;
+ self->ad = byte & (1 << 5) ? 1 : 0;
+ self->cd = byte & (1 << 4) ? 1 : 0;
+ self->rcode = byte & 0xf;
+ self->have_ra = self->have_z = self->have_ad = self->have_cd = self->have_rcode = 1;
+
+ need16(self->qdcount, self->at, self->left);
+ self->have_qdcount = 1;
+
+ need16(self->ancount, self->at, self->left);
+ self->have_ancount = 1;
+
+ need16(self->nscount, self->at, self->left);
+ self->have_nscount = 1;
+
+ need16(self->arcount, self->at, self->left);
+ self->have_arcount = 1;
+
+ return 0;
+ }
+
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+}
+
+static inline size_t _rdata_labels(uint16_t type)
+{
+ switch (type) {
+ case CORE_OBJECT_DNS_TYPE_NS:
+ case CORE_OBJECT_DNS_TYPE_MD:
+ case CORE_OBJECT_DNS_TYPE_MF:
+ case CORE_OBJECT_DNS_TYPE_CNAME:
+ case CORE_OBJECT_DNS_TYPE_MB:
+ case CORE_OBJECT_DNS_TYPE_MG:
+ case CORE_OBJECT_DNS_TYPE_MR:
+ case CORE_OBJECT_DNS_TYPE_PTR:
+ case CORE_OBJECT_DNS_TYPE_NXT:
+ case CORE_OBJECT_DNS_TYPE_DNAME:
+ case CORE_OBJECT_DNS_TYPE_NSEC:
+ case CORE_OBJECT_DNS_TYPE_TKEY:
+ case CORE_OBJECT_DNS_TYPE_TSIG:
+ return 1;
+
+ case CORE_OBJECT_DNS_TYPE_SOA:
+ case CORE_OBJECT_DNS_TYPE_MINFO:
+ case CORE_OBJECT_DNS_TYPE_RP:
+ case CORE_OBJECT_DNS_TYPE_TALINK:
+ return 2;
+
+ case CORE_OBJECT_DNS_TYPE_MX:
+ case CORE_OBJECT_DNS_TYPE_AFSDB:
+ case CORE_OBJECT_DNS_TYPE_RT:
+ case CORE_OBJECT_DNS_TYPE_KX:
+ case CORE_OBJECT_DNS_TYPE_LP:
+ return 1;
+
+ case CORE_OBJECT_DNS_TYPE_PX:
+ return 2;
+
+ case CORE_OBJECT_DNS_TYPE_SIG:
+ case CORE_OBJECT_DNS_TYPE_RRSIG:
+ return 1;
+
+ case CORE_OBJECT_DNS_TYPE_SRV:
+ return 1;
+
+ case CORE_OBJECT_DNS_TYPE_NAPTR:
+ return 1;
+
+ case CORE_OBJECT_DNS_TYPE_HIP:
+ return 1;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static inline size_t _label(core_object_dns_t* self, core_object_dns_label_t* label, size_t labels)
+{
+ size_t n;
+
+ for (n = 0; self->left && n < labels; n++) {
+ core_object_dns_label_t* l = &label[n];
+ *l = _defaults_label;
+
+ need8(l->length, self->at, self->left);
+
+ if ((l->length & 0xc0) == 0xc0) {
+ need8(l->offset, self->at, self->left);
+ l->offset |= (l->length & 0x3f) << 8;
+ l->have_offset = 1;
+ return n;
+ } else if (l->length & 0xc0) {
+ l->extension_bits = l->length >> 6;
+ l->have_extension_bits = 1;
+ return n;
+ } else if (l->length) {
+ l->have_length = 1;
+
+ l->offset = self->at - self->payload - 1;
+ advancexb(l->length, self->at, self->left);
+ l->have_dn = 1;
+ } else {
+ l->is_end = 1;
+ return n;
+ }
+ }
+
+ return n;
+}
+
+int core_object_dns_parse_q(core_object_dns_t* self, core_object_dns_q_t* q, core_object_dns_label_t* label, size_t labels)
+{
+ mlassert_self();
+ mlassert(q, "q is nil");
+ mlassert(label, "label is nil");
+ mlassert(labels, "labels is zero");
+ mlassert(self->at, "at is nil");
+
+ for (;;) {
+ *q = _defaults_q;
+ q->labels = _label(self, label, labels);
+ if (q->labels < labels) {
+ core_object_dns_label_t* l = &label[q->labels];
+ if (!(l->have_offset | l->have_extension_bits | l->is_end)) {
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+ }
+ } else {
+ mlwarning("need more labels, aborting DNS parsing");
+ return _ERR_NEEDLABELS;
+ }
+ q->labels++;
+
+ need16(q->type, self->at, self->left);
+ q->have_type = 1;
+
+ need16(q->class, self->at, self->left);
+ q->have_class = 1;
+
+ return 0;
+ }
+
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+}
+
+int core_object_dns_parse_rr(core_object_dns_t* self, core_object_dns_rr_t* rr, core_object_dns_label_t* label, size_t labels)
+{
+ size_t rdata_label_sets;
+ mlassert_self();
+ mlassert(rr, "rr is nil");
+ mlassert(label, "label is nil");
+ mlassert(labels, "labels is zero");
+ mlassert(self->at, "at is nil");
+
+ for (;;) {
+ *rr = _defaults_rr;
+ rr->labels = _label(self, label, labels);
+ if (rr->labels < labels) {
+ core_object_dns_label_t* l = &label[rr->labels];
+ if (!(l->have_offset | l->have_extension_bits | l->is_end)) {
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+ }
+ } else {
+ mlwarning("need more labels, aborting DNS parsing");
+ return _ERR_NEEDLABELS;
+ }
+ rr->labels++;
+
+ need16(rr->type, self->at, self->left);
+ rr->have_type = 1;
+
+ need16(rr->class, self->at, self->left);
+ rr->have_class = 1;
+
+ need32(rr->ttl, self->at, self->left);
+ rr->have_ttl = 1;
+
+ need16(rr->rdlength, self->at, self->left);
+ rr->have_rdlength = 1;
+
+ rr->rdata_offset = self->at - self->payload;
+ if (!(rdata_label_sets = _rdata_labels(rr->type))) {
+ advancexb(rr->rdlength, self->at, self->left);
+ rr->have_rdata = 1;
+ return 0;
+ }
+
+ switch (rr->type) {
+ case CORE_OBJECT_DNS_TYPE_MX:
+ case CORE_OBJECT_DNS_TYPE_AFSDB:
+ case CORE_OBJECT_DNS_TYPE_RT:
+ case CORE_OBJECT_DNS_TYPE_KX:
+ case CORE_OBJECT_DNS_TYPE_LP:
+ case CORE_OBJECT_DNS_TYPE_PX:
+ advancexb(2, self->at, self->left);
+ break;
+
+ case CORE_OBJECT_DNS_TYPE_SIG:
+ case CORE_OBJECT_DNS_TYPE_RRSIG:
+ advancexb(18, self->at, self->left);
+ break;
+
+ case CORE_OBJECT_DNS_TYPE_SRV:
+ advancexb(6, self->at, self->left);
+ break;
+
+ case CORE_OBJECT_DNS_TYPE_NAPTR: {
+ uint8_t naptr_length;
+
+ advancexb(4, self->at, self->left);
+ need8(naptr_length, self->at, self->left);
+ advancexb(naptr_length, self->at, self->left);
+ need8(naptr_length, self->at, self->left);
+ advancexb(naptr_length, self->at, self->left);
+ need8(naptr_length, self->at, self->left);
+ advancexb(naptr_length, self->at, self->left);
+ } break;
+
+ case CORE_OBJECT_DNS_TYPE_HIP: {
+ uint8_t hit_length;
+ uint16_t pk_length;
+
+ need8(hit_length, self->at, self->left);
+ advancexb(1, self->at, self->left);
+ need16(pk_length, self->at, self->left);
+ advancexb(hit_length, self->at, self->left);
+ advancexb(pk_length, self->at, self->left);
+
+ if (self->at - self->payload >= rr->rdata_offset + rr->rdlength) {
+ rdata_label_sets = 0;
+ }
+ } break;
+ }
+
+ while (rdata_label_sets) {
+ rr->rdata_labels += _label(self, &label[rr->labels + rr->rdata_labels], labels - rr->labels - rr->rdata_labels);
+ if (rr->labels + rr->rdata_labels < labels) {
+ core_object_dns_label_t* l = &label[rr->labels + rr->rdata_labels];
+ if (!(l->have_offset | l->have_extension_bits | l->is_end)) {
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+ }
+ } else {
+ mlwarning("need more labels, aborting DNS parsing");
+ return _ERR_NEEDLABELS;
+ }
+ rr->rdata_labels++;
+
+ if (rr->type == CORE_OBJECT_DNS_TYPE_HIP && self->at - self->payload < rr->rdata_offset + rr->rdlength) {
+ continue;
+ }
+
+ rdata_label_sets--;
+ }
+
+ if (self->at - self->payload < rr->rdata_offset + rr->rdlength) {
+ rr->padding_offset = self->at - self->payload;
+ rr->padding_length = rr->rdlength - (rr->padding_offset - rr->rdata_offset);
+
+ advancexb(rr->padding_length, self->at, self->left);
+
+ /*
+ * TODO:
+ *
+ * This can indicate padding but we do not set that we have padding
+ * yet because we need to fully understand all record types before
+ * that and process valid data after the labels
+ *
+ rr->have_padding = 1;
+ */
+ } else if (self->at - self->payload > rr->rdata_offset + rr->rdlength) {
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+ }
+ rr->have_rdata = 1;
+
+ return 0;
+ }
+
+ // TODO: error here on malformed/truncated? could be quite spammy
+ return _ERR_MALFORMED;
+}
diff --git a/include/dnsjit/core/object/dns.h b/include/dnsjit/core/object/dns.h
new file mode 100644
index 0000000..68a2dc9
--- /dev/null
+++ b/include/dnsjit/core/object/dns.h
@@ -0,0 +1,184 @@
+/*
+ * 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 <dnsjit/core/log.h>
+#include <dnsjit/core/object.h>
+
+#ifndef __dnsjit_core_object_dns_h
+#define __dnsjit_core_object_dns_h
+
+#include <netinet/in.h>
+#include <sys/types.h>
+
+#include <dnsjit/core/object/dns.hh>
+
+#define CORE_OBJECT_DNS_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_DNS, prev) \
+ , \
+ 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ }
+
+/*
+ * 2016-12-09 https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
+ */
+
+#define CORE_OBJECT_DNS_CLASS_IN 1
+#define CORE_OBJECT_DNS_CLASS_CH 3
+#define CORE_OBJECT_DNS_CLASS_HS 4
+#define CORE_OBJECT_DNS_CLASS_NONE 254
+#define CORE_OBJECT_DNS_CLASS_ANY 255
+
+#define CORE_OBJECT_DNS_TYPE_A 1
+#define CORE_OBJECT_DNS_TYPE_NS 2
+#define CORE_OBJECT_DNS_TYPE_MD 3
+#define CORE_OBJECT_DNS_TYPE_MF 4
+#define CORE_OBJECT_DNS_TYPE_CNAME 5
+#define CORE_OBJECT_DNS_TYPE_SOA 6
+#define CORE_OBJECT_DNS_TYPE_MB 7
+#define CORE_OBJECT_DNS_TYPE_MG 8
+#define CORE_OBJECT_DNS_TYPE_MR 9
+#define CORE_OBJECT_DNS_TYPE_NULL 10
+#define CORE_OBJECT_DNS_TYPE_WKS 11
+#define CORE_OBJECT_DNS_TYPE_PTR 12
+#define CORE_OBJECT_DNS_TYPE_HINFO 13
+#define CORE_OBJECT_DNS_TYPE_MINFO 14
+#define CORE_OBJECT_DNS_TYPE_MX 15
+#define CORE_OBJECT_DNS_TYPE_TXT 16
+#define CORE_OBJECT_DNS_TYPE_RP 17
+#define CORE_OBJECT_DNS_TYPE_AFSDB 18
+#define CORE_OBJECT_DNS_TYPE_X25 19
+#define CORE_OBJECT_DNS_TYPE_ISDN 20
+#define CORE_OBJECT_DNS_TYPE_RT 21
+#define CORE_OBJECT_DNS_TYPE_NSAP 22
+#define CORE_OBJECT_DNS_TYPE_NSAP_PTR 23
+#define CORE_OBJECT_DNS_TYPE_SIG 24
+#define CORE_OBJECT_DNS_TYPE_KEY 25
+#define CORE_OBJECT_DNS_TYPE_PX 26
+#define CORE_OBJECT_DNS_TYPE_GPOS 27
+#define CORE_OBJECT_DNS_TYPE_AAAA 28
+#define CORE_OBJECT_DNS_TYPE_LOC 29
+#define CORE_OBJECT_DNS_TYPE_NXT 30
+#define CORE_OBJECT_DNS_TYPE_EID 31
+#define CORE_OBJECT_DNS_TYPE_NIMLOC 32
+#define CORE_OBJECT_DNS_TYPE_SRV 33
+#define CORE_OBJECT_DNS_TYPE_ATMA 34
+#define CORE_OBJECT_DNS_TYPE_NAPTR 35
+#define CORE_OBJECT_DNS_TYPE_KX 36
+#define CORE_OBJECT_DNS_TYPE_CERT 37
+#define CORE_OBJECT_DNS_TYPE_A6 38
+#define CORE_OBJECT_DNS_TYPE_DNAME 39
+#define CORE_OBJECT_DNS_TYPE_SINK 40
+#define CORE_OBJECT_DNS_TYPE_OPT 41
+#define CORE_OBJECT_DNS_TYPE_APL 42
+#define CORE_OBJECT_DNS_TYPE_DS 43
+#define CORE_OBJECT_DNS_TYPE_SSHFP 44
+#define CORE_OBJECT_DNS_TYPE_IPSECKEY 45
+#define CORE_OBJECT_DNS_TYPE_RRSIG 46
+#define CORE_OBJECT_DNS_TYPE_NSEC 47
+#define CORE_OBJECT_DNS_TYPE_DNSKEY 48
+#define CORE_OBJECT_DNS_TYPE_DHCID 49
+#define CORE_OBJECT_DNS_TYPE_NSEC3 50
+#define CORE_OBJECT_DNS_TYPE_NSEC3PARAM 51
+#define CORE_OBJECT_DNS_TYPE_TLSA 52
+#define CORE_OBJECT_DNS_TYPE_SMIMEA 53
+#define CORE_OBJECT_DNS_TYPE_HIP 55
+#define CORE_OBJECT_DNS_TYPE_NINFO 56
+#define CORE_OBJECT_DNS_TYPE_RKEY 57
+#define CORE_OBJECT_DNS_TYPE_TALINK 58
+#define CORE_OBJECT_DNS_TYPE_CDS 59
+#define CORE_OBJECT_DNS_TYPE_CDNSKEY 60
+#define CORE_OBJECT_DNS_TYPE_OPENPGPKEY 61
+#define CORE_OBJECT_DNS_TYPE_CSYNC 62
+#define CORE_OBJECT_DNS_TYPE_SPF 99
+#define CORE_OBJECT_DNS_TYPE_UINFO 100
+#define CORE_OBJECT_DNS_TYPE_UID 101
+#define CORE_OBJECT_DNS_TYPE_GID 102
+#define CORE_OBJECT_DNS_TYPE_UNSPEC 103
+#define CORE_OBJECT_DNS_TYPE_NID 104
+#define CORE_OBJECT_DNS_TYPE_L32 105
+#define CORE_OBJECT_DNS_TYPE_L64 106
+#define CORE_OBJECT_DNS_TYPE_LP 107
+#define CORE_OBJECT_DNS_TYPE_EUI48 108
+#define CORE_OBJECT_DNS_TYPE_EUI64 109
+#define CORE_OBJECT_DNS_TYPE_TKEY 249
+#define CORE_OBJECT_DNS_TYPE_TSIG 250
+#define CORE_OBJECT_DNS_TYPE_IXFR 251
+#define CORE_OBJECT_DNS_TYPE_AXFR 252
+#define CORE_OBJECT_DNS_TYPE_MAILB 253
+#define CORE_OBJECT_DNS_TYPE_MAILA 254
+#define CORE_OBJECT_DNS_TYPE_ANY 255
+#define CORE_OBJECT_DNS_TYPE_URI 256
+#define CORE_OBJECT_DNS_TYPE_CAA 257
+#define CORE_OBJECT_DNS_TYPE_AVC 258
+#define CORE_OBJECT_DNS_TYPE_TA 32768
+#define CORE_OBJECT_DNS_TYPE_DLV 32769
+
+#define CORE_OBJECT_DNS_OPCODE_QUERY 0
+#define CORE_OBJECT_DNS_OPCODE_IQUERY 1
+#define CORE_OBJECT_DNS_OPCODE_STATUS 2
+#define CORE_OBJECT_DNS_OPCODE_NOTIFY 4
+#define CORE_OBJECT_DNS_OPCODE_UPDATE 5
+
+#define CORE_OBJECT_DNS_RCODE_NOERROR 0
+#define CORE_OBJECT_DNS_RCODE_FORMERR 1
+#define CORE_OBJECT_DNS_RCODE_SERVFAIL 2
+#define CORE_OBJECT_DNS_RCODE_NXDOMAIN 3
+#define CORE_OBJECT_DNS_RCODE_NOTIMP 4
+#define CORE_OBJECT_DNS_RCODE_REFUSED 5
+#define CORE_OBJECT_DNS_RCODE_YXDOMAIN 6
+#define CORE_OBJECT_DNS_RCODE_YXRRSET 7
+#define CORE_OBJECT_DNS_RCODE_NXRRSET 8
+#define CORE_OBJECT_DNS_RCODE_NOTAUTH 9
+#define CORE_OBJECT_DNS_RCODE_NOTZONE 10
+#define CORE_OBJECT_DNS_RCODE_BADVERS 16
+#define CORE_OBJECT_DNS_RCODE_BADSIG 16
+#define CORE_OBJECT_DNS_RCODE_BADKEY 17
+#define CORE_OBJECT_DNS_RCODE_BADTIME 18
+#define CORE_OBJECT_DNS_RCODE_BADMODE 19
+#define CORE_OBJECT_DNS_RCODE_BADNAME 20
+#define CORE_OBJECT_DNS_RCODE_BADALG 21
+#define CORE_OBJECT_DNS_RCODE_BADTRUNC 22
+#define CORE_OBJECT_DNS_RCODE_BADCOOKIE 23
+
+#define CORE_OBJECT_DNS_AFSDB_SUBTYPE_AFS3LOCSRV 1
+#define CORE_OBJECT_DNS_AFSDB_SUBTYPE_DCENCA_ROOT 2
+
+#define CORE_OBJECT_DNS_DHCID_TYPE_1OCTET 0
+#define CORE_OBJECT_DNS_DHCID_TYPE_DATAOCTET 1
+#define CORE_OBJECT_DNS_DHCID_TYPE_CLIENT_DUID 2
+
+#define CORE_OBJECT_DNS_EDNS0_OPT_LLQ 1
+#define CORE_OBJECT_DNS_EDNS0_OPT_UL 2
+#define CORE_OBJECT_DNS_EDNS0_OPT_NSID 3
+#define CORE_OBJECT_DNS_EDNS0_OPT_DAU 5
+#define CORE_OBJECT_DNS_EDNS0_OPT_DHU 6
+#define CORE_OBJECT_DNS_EDNS0_OPT_N3U 7
+#define CORE_OBJECT_DNS_EDNS0_OPT_CLIENT_SUBNET 8
+#define CORE_OBJECT_DNS_EDNS0_OPT_EXPIRE 9
+#define CORE_OBJECT_DNS_EDNS0_OPT_COOKIE 10
+#define CORE_OBJECT_DNS_EDNS0_OPT_TCP_KEEPALIVE 11
+#define CORE_OBJECT_DNS_EDNS0_OPT_PADDING 12
+#define CORE_OBJECT_DNS_EDNS0_OPT_CHAIN 13
+#define CORE_OBJECT_DNS_EDNS0_OPT_DEVICEID 26946
+
+#endif
diff --git a/include/dnsjit/core/object/dns.hh b/include/dnsjit/core/object/dns.hh
new file mode 100644
index 0000000..dfe9719
--- /dev/null
+++ b/include/dnsjit/core/object/dns.hh
@@ -0,0 +1,119 @@
+/*
+ * 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.object_h")
+
+typedef struct core_object_dns_label {
+ uint8_t is_end;
+ uint8_t have_length;
+ uint8_t have_offset;
+ uint8_t have_extension_bits;
+ uint8_t have_dn;
+ uint8_t extension_bits;
+
+ uint8_t length;
+ uint16_t offset;
+} core_object_dns_label_t;
+
+typedef struct core_object_dns_rr {
+ uint8_t have_type;
+ uint8_t have_class;
+ uint8_t have_ttl;
+ uint8_t have_rdlength;
+ uint8_t have_rdata;
+ uint8_t have_rdata_labels;
+ uint8_t have_padding;
+
+ uint16_t type;
+ uint16_t class;
+ uint32_t ttl;
+ uint16_t rdlength;
+
+ size_t labels;
+ size_t rdata_offset;
+ size_t rdata_labels;
+ size_t padding_offset;
+ size_t padding_length;
+} core_object_dns_rr_t;
+
+typedef struct core_object_dns_q {
+ uint8_t have_type;
+ uint8_t have_class;
+
+ uint16_t type;
+ uint16_t class;
+
+ size_t labels;
+} core_object_dns_q_t;
+
+typedef struct core_object_dns {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ int includes_dnslen;
+ const uint8_t *payload, *at;
+ size_t len, left;
+
+ uint8_t have_dnslen;
+ uint8_t have_id;
+ uint8_t have_qr;
+ uint8_t have_opcode;
+ uint8_t have_aa;
+ uint8_t have_tc;
+ uint8_t have_rd;
+ uint8_t have_ra;
+ uint8_t have_z;
+ uint8_t have_ad;
+ uint8_t have_cd;
+ uint8_t have_rcode;
+ uint8_t have_qdcount;
+ uint8_t have_ancount;
+ uint8_t have_nscount;
+ uint8_t have_arcount;
+
+ uint16_t dnslen;
+ uint16_t id;
+ int8_t qr;
+ uint8_t opcode;
+ uint8_t aa;
+ uint8_t tc;
+ uint8_t rd;
+ uint8_t ra;
+ uint8_t z;
+ uint8_t ad;
+ uint8_t cd;
+ uint8_t rcode;
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+} core_object_dns_t;
+
+core_log_t* core_object_dns_log();
+
+core_object_dns_t* core_object_dns_new();
+core_object_dns_t* core_object_dns_copy(const core_object_dns_t* self);
+void core_object_dns_free(core_object_dns_t* self);
+void core_object_dns_reset(core_object_dns_t* self, const core_object_t* obj);
+
+int core_object_dns_parse_header(core_object_dns_t* self);
+int core_object_dns_parse_q(core_object_dns_t* self, core_object_dns_q_t* q, core_object_dns_label_t* label, size_t labels);
+int core_object_dns_parse_rr(core_object_dns_t* self, core_object_dns_rr_t* rr, core_object_dns_label_t* label, size_t labels);
diff --git a/include/dnsjit/core/object/dns.lua b/include/dnsjit/core/object/dns.lua
new file mode 100644
index 0000000..e8fb12b
--- /dev/null
+++ b/include/dnsjit/core/object/dns.lua
@@ -0,0 +1,797 @@
+-- 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.core.object.dns
+-- Container of a DNS message
+-- .SS Parse DNS header and check if query or response
+-- local dns = require("dnsjit.core.object.dns").new(payload)
+-- if dns:parse_header() == 0 then
+-- if dns.qr == 0 then
+-- print(dns.id, dns.opcode_tostring(dns.opcode))
+-- else
+-- print(dns.id, dns.rcode_tostring(dns.rcode))
+-- end
+-- end
+-- .SS Print a DNS payload
+-- local dns = require("dnsjit.core.object.dns").new(payload)
+-- dns:print()
+-- .SS Parse a DNS payload
+-- local dns = require("dnsjit.core.object.dns").new(payload)
+-- local qs, q_labels, rrs, rr_labels = dns:parse()
+-- if qs and q_labels then
+-- ...
+-- if rrs and rr_labels then
+-- ...
+-- end
+-- end
+--
+-- The object that describes a DNS message.
+-- .SS Attributes
+-- .TP
+-- includes_dnslen
+-- If non-zero then this indicates that the DNS length is included in the
+-- payload (for example if the transport is TCP) and will affect parsing of it.
+-- .TP
+-- have_dnslen
+-- Set if the dnslen was included in the payload and could be read.
+-- .TP
+-- have_id
+-- Set if there is a DNS ID.
+-- .TP
+-- have_qr
+-- Set if there is a QR flag.
+-- .TP
+-- have_opcode
+-- Set if there is an OPCODE.
+-- .TP
+-- have_aa
+-- Set if there is a AA flag.
+-- .TP
+-- have_tc
+-- Set if there is a TC flag.
+-- .TP
+-- have_rd
+-- Set if there is a RD flag.
+-- .TP
+-- have_ra
+-- Set if there is a RA flag.
+-- .TP
+-- have_z
+-- Set if there is a Z flag.
+-- .TP
+-- have_ad
+-- Set if there is a AD flag.
+-- .TP
+-- have_cd
+-- Set if there is a CD flag.
+-- .TP
+-- have_rcode
+-- Set if there is a RCODE.
+-- .TP
+-- have_qdcount
+-- Set if there is an QDCOUNT.
+-- .TP
+-- have_ancount
+-- Set if there is an ANCOUNT.
+-- .TP
+-- have_nscount
+-- Set if there is a NSCOUNT.
+-- .TP
+-- have_arcount
+-- Set if there is an ARCOUNT.
+-- .TP
+-- dnslen
+-- The DNS length found in the payload.
+-- .TP
+-- id
+-- The DNS ID.
+-- .TP
+-- qr
+-- The QR flag.
+-- .TP
+-- opcode
+-- The OPCODE.
+-- .TP
+-- aa
+-- The AA flag.
+-- .TP
+-- tc
+-- The TC flag.
+-- .TP
+-- rd
+-- The RD flag.
+-- .TP
+-- ra
+-- The RA flag.
+-- .TP
+-- z
+-- The Z flag.
+-- .TP
+-- ad
+-- The AD flag.
+-- .TP
+-- cd
+-- The CD flag.
+-- .TP
+-- rcode
+-- The RCODE.
+-- .TP
+-- qdcount
+-- The QDCOUNT.
+-- .TP
+-- ancount
+-- The ANCOUNT.
+-- .TP
+-- nscount
+-- The NSCOUNT.
+-- .TP
+-- arcount
+-- The ARCOUNT.
+-- .SS Constants
+-- The following tables exists for DNS parameters, taken from
+-- .I https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
+-- on the 2016-12-09.
+-- .LP
+-- .IR CLASS ,
+-- .IR CLASS_STR ,
+-- .IR TYPE ,
+-- .IR TYPE_STR ,
+-- .IR OPCODE ,
+-- .IR OPCODE_STR ,
+-- .IR RCODE ,
+-- .IR RCODE_STR ,
+-- .IR AFSDB ,
+-- .IR AFSDB_STR ,
+-- .IR DHCID ,
+-- .IR DHCID_STR ,
+-- .IR ENDS0 ,
+-- .IR ENDS0_STR
+-- .LP
+-- The
+-- .I *_STR
+-- tables can be used to get a textual representation of the numbers, see also
+-- .IR class_tostring() ,
+-- .IR type_tostring() ,
+-- .IR opcode_tostring() ,
+-- .IR rcode_tostring() ,
+-- .IR afsdb_tostring() ,
+-- .I dhcid_tostring()
+-- and
+-- .IR edns0_tostring() .
+module(...,package.seeall)
+
+require("dnsjit.core.object.dns_h")
+local label = require("dnsjit.core.object.dns.label")
+local Q = require("dnsjit.core.object.dns.q")
+local RR = require("dnsjit.core.object.dns.rr")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_dns_t"
+local core_object_dns_t
+local Dns = {
+ CLASS = {
+ IN = 1,
+ CH = 3,
+ HS = 4,
+ NONE = 254,
+ ANY = 255,
+ },
+ TYPE = {
+ A = 1,
+ NS = 2,
+ MD = 3,
+ MF = 4,
+ CNAME = 5,
+ SOA = 6,
+ MB = 7,
+ MG = 8,
+ MR = 9,
+ NULL = 10,
+ WKS = 11,
+ PTR = 12,
+ HINFO = 13,
+ MINFO = 14,
+ MX = 15,
+ TXT = 16,
+ RP = 17,
+ AFSDB = 18,
+ X25 = 19,
+ ISDN = 20,
+ RT = 21,
+ NSAP = 22,
+ NSAP_PTR = 23,
+ SIG = 24,
+ KEY = 25,
+ PX = 26,
+ GPOS = 27,
+ AAAA = 28,
+ LOC = 29,
+ NXT = 30,
+ EID = 31,
+ NIMLOC = 32,
+ SRV = 33,
+ ATMA = 34,
+ NAPTR = 35,
+ KX = 36,
+ CERT = 37,
+ A6 = 38,
+ DNAME = 39,
+ SINK = 40,
+ OPT = 41,
+ APL = 42,
+ DS = 43,
+ SSHFP = 44,
+ IPSECKEY = 45,
+ RRSIG = 46,
+ NSEC = 47,
+ DNSKEY = 48,
+ DHCID = 49,
+ NSEC3 = 50,
+ NSEC3PARAM = 51,
+ TLSA = 52,
+ SMIMEA = 53,
+ HIP = 55,
+ NINFO = 56,
+ RKEY = 57,
+ TALINK = 58,
+ CDS = 59,
+ CDNSKEY = 60,
+ OPENPGPKEY = 61,
+ CSYNC = 62,
+ SPF = 99,
+ UINFO = 100,
+ UID = 101,
+ GID = 102,
+ UNSPEC = 103,
+ NID = 104,
+ L32 = 105,
+ L64 = 106,
+ LP = 107,
+ EUI48 = 108,
+ EUI64 = 109,
+ TKEY = 249,
+ TSIG = 250,
+ IXFR = 251,
+ AXFR = 252,
+ MAILB = 253,
+ MAILA = 254,
+ ANY = 255,
+ URI = 256,
+ CAA = 257,
+ AVC = 258,
+ TA = 32768,
+ DLV = 32769,
+ },
+ OPCODE = {
+ QUERY = 0,
+ IQUERY = 1,
+ STATUS = 2,
+ NOTIFY = 4,
+ UPDATE = 5,
+ },
+ RCODE = {
+ NOERROR = 0,
+ FORMERR = 1,
+ SERVFAIL = 2,
+ NXDOMAIN = 3,
+ NOTIMP = 4,
+ REFUSED = 5,
+ YXDOMAIN = 6,
+ YXRRSET = 7,
+ NXRRSET = 8,
+ NOTAUTH = 9,
+ NOTZONE = 10,
+ BADVERS = 16,
+ BADSIG = 16,
+ BADKEY = 17,
+ BADTIME = 18,
+ BADMODE = 19,
+ BADNAME = 20,
+ BADALG = 21,
+ BADTRUNC = 22,
+ BADCOOKIE = 23,
+ },
+ AFSDB = {
+ SUBTYPE_AFS3LOCSRV = 1,
+ SUBTYPE_DCENCA_ROOT = 2,
+ },
+ DHCID = {
+ TYPE_1OCTET = 0,
+ TYPE_DATAOCTET = 1,
+ TYPE_CLIENT_DUID = 2,
+ },
+ EDNS0 = {
+ OPT_LLQ = 1,
+ OPT_UL = 2,
+ OPT_NSID = 3,
+ OPT_DAU = 5,
+ OPT_DHU = 6,
+ OPT_N3U = 7,
+ OPT_CLIENT_SUBNET = 8,
+ OPT_EXPIRE = 9,
+ OPT_COOKIE = 10,
+ OPT_TCP_KEEPALIVE = 11,
+ OPT_PADDING = 12,
+ OPT_CHAIN = 13,
+ OPT_DEVICEID = 26946,
+ },
+}
+local _CLASS = {}
+_CLASS[Dns.CLASS.IN] = "IN"
+_CLASS[Dns.CLASS.CH] = "CH"
+_CLASS[Dns.CLASS.HS] = "HS"
+_CLASS[Dns.CLASS.NONE] = "NONE"
+_CLASS[Dns.CLASS.ANY] = "ANY"
+local _TYPE = {}
+_TYPE[Dns.TYPE.A] = "A"
+_TYPE[Dns.TYPE.NS] = "NS"
+_TYPE[Dns.TYPE.MD] = "MD"
+_TYPE[Dns.TYPE.MF] = "MF"
+_TYPE[Dns.TYPE.CNAME] = "CNAME"
+_TYPE[Dns.TYPE.SOA] = "SOA"
+_TYPE[Dns.TYPE.MB] = "MB"
+_TYPE[Dns.TYPE.MG] = "MG"
+_TYPE[Dns.TYPE.MR] = "MR"
+_TYPE[Dns.TYPE.NULL] = "NULL"
+_TYPE[Dns.TYPE.WKS] = "WKS"
+_TYPE[Dns.TYPE.PTR] = "PTR"
+_TYPE[Dns.TYPE.HINFO] = "HINFO"
+_TYPE[Dns.TYPE.MINFO] = "MINFO"
+_TYPE[Dns.TYPE.MX] = "MX"
+_TYPE[Dns.TYPE.TXT] = "TXT"
+_TYPE[Dns.TYPE.RP] = "RP"
+_TYPE[Dns.TYPE.AFSDB] = "AFSDB"
+_TYPE[Dns.TYPE.X25] = "X25"
+_TYPE[Dns.TYPE.ISDN] = "ISDN"
+_TYPE[Dns.TYPE.RT] = "RT"
+_TYPE[Dns.TYPE.NSAP] = "NSAP"
+_TYPE[Dns.TYPE.NSAP_PTR] = "NSAP_PTR"
+_TYPE[Dns.TYPE.SIG] = "SIG"
+_TYPE[Dns.TYPE.KEY] = "KEY"
+_TYPE[Dns.TYPE.PX] = "PX"
+_TYPE[Dns.TYPE.GPOS] = "GPOS"
+_TYPE[Dns.TYPE.AAAA] = "AAAA"
+_TYPE[Dns.TYPE.LOC] = "LOC"
+_TYPE[Dns.TYPE.NXT] = "NXT"
+_TYPE[Dns.TYPE.EID] = "EID"
+_TYPE[Dns.TYPE.NIMLOC] = "NIMLOC"
+_TYPE[Dns.TYPE.SRV] = "SRV"
+_TYPE[Dns.TYPE.ATMA] = "ATMA"
+_TYPE[Dns.TYPE.NAPTR] = "NAPTR"
+_TYPE[Dns.TYPE.KX] = "KX"
+_TYPE[Dns.TYPE.CERT] = "CERT"
+_TYPE[Dns.TYPE.A6] = "A6"
+_TYPE[Dns.TYPE.DNAME] = "DNAME"
+_TYPE[Dns.TYPE.SINK] = "SINK"
+_TYPE[Dns.TYPE.OPT] = "OPT"
+_TYPE[Dns.TYPE.APL] = "APL"
+_TYPE[Dns.TYPE.DS] = "DS"
+_TYPE[Dns.TYPE.SSHFP] = "SSHFP"
+_TYPE[Dns.TYPE.IPSECKEY] = "IPSECKEY"
+_TYPE[Dns.TYPE.RRSIG] = "RRSIG"
+_TYPE[Dns.TYPE.NSEC] = "NSEC"
+_TYPE[Dns.TYPE.DNSKEY] = "DNSKEY"
+_TYPE[Dns.TYPE.DHCID] = "DHCID"
+_TYPE[Dns.TYPE.NSEC3] = "NSEC3"
+_TYPE[Dns.TYPE.NSEC3PARAM] = "NSEC3PARAM"
+_TYPE[Dns.TYPE.TLSA] = "TLSA"
+_TYPE[Dns.TYPE.SMIMEA] = "SMIMEA"
+_TYPE[Dns.TYPE.HIP] = "HIP"
+_TYPE[Dns.TYPE.NINFO] = "NINFO"
+_TYPE[Dns.TYPE.RKEY] = "RKEY"
+_TYPE[Dns.TYPE.TALINK] = "TALINK"
+_TYPE[Dns.TYPE.CDS] = "CDS"
+_TYPE[Dns.TYPE.CDNSKEY] = "CDNSKEY"
+_TYPE[Dns.TYPE.OPENPGPKEY] = "OPENPGPKEY"
+_TYPE[Dns.TYPE.CSYNC] = "CSYNC"
+_TYPE[Dns.TYPE.SPF] = "SPF"
+_TYPE[Dns.TYPE.UINFO] = "UINFO"
+_TYPE[Dns.TYPE.UID] = "UID"
+_TYPE[Dns.TYPE.GID] = "GID"
+_TYPE[Dns.TYPE.UNSPEC] = "UNSPEC"
+_TYPE[Dns.TYPE.NID] = "NID"
+_TYPE[Dns.TYPE.L32] = "L32"
+_TYPE[Dns.TYPE.L64] = "L64"
+_TYPE[Dns.TYPE.LP] = "LP"
+_TYPE[Dns.TYPE.EUI48] = "EUI48"
+_TYPE[Dns.TYPE.EUI64] = "EUI64"
+_TYPE[Dns.TYPE.TKEY] = "TKEY"
+_TYPE[Dns.TYPE.TSIG] = "TSIG"
+_TYPE[Dns.TYPE.IXFR] = "IXFR"
+_TYPE[Dns.TYPE.AXFR] = "AXFR"
+_TYPE[Dns.TYPE.MAILB] = "MAILB"
+_TYPE[Dns.TYPE.MAILA] = "MAILA"
+_TYPE[Dns.TYPE.ANY] = "ANY"
+_TYPE[Dns.TYPE.URI] = "URI"
+_TYPE[Dns.TYPE.CAA] = "CAA"
+_TYPE[Dns.TYPE.AVC] = "AVC"
+_TYPE[Dns.TYPE.TA] = "TA"
+_TYPE[Dns.TYPE.DLV] = "DLV"
+local _OPCODE = {}
+_OPCODE[Dns.OPCODE.QUERY] = "QUERY"
+_OPCODE[Dns.OPCODE.IQUERY] = "IQUERY"
+_OPCODE[Dns.OPCODE.STATUS] = "STATUS"
+_OPCODE[Dns.OPCODE.NOTIFY] = "NOTIFY"
+_OPCODE[Dns.OPCODE.UPDATE] = "UPDATE"
+local _RCODE = {}
+_RCODE[Dns.RCODE.NOERROR] = "NOERROR"
+_RCODE[Dns.RCODE.FORMERR] = "FORMERR"
+_RCODE[Dns.RCODE.SERVFAIL] = "SERVFAIL"
+_RCODE[Dns.RCODE.NXDOMAIN] = "NXDOMAIN"
+_RCODE[Dns.RCODE.NOTIMP] = "NOTIMP"
+_RCODE[Dns.RCODE.REFUSED] = "REFUSED"
+_RCODE[Dns.RCODE.YXDOMAIN] = "YXDOMAIN"
+_RCODE[Dns.RCODE.YXRRSET] = "YXRRSET"
+_RCODE[Dns.RCODE.NXRRSET] = "NXRRSET"
+_RCODE[Dns.RCODE.NOTAUTH] = "NOTAUTH"
+_RCODE[Dns.RCODE.NOTZONE] = "NOTZONE"
+_RCODE[Dns.RCODE.BADVERS] = "BADVERS"
+_RCODE[Dns.RCODE.BADSIG] = "BADSIG"
+_RCODE[Dns.RCODE.BADKEY] = "BADKEY"
+_RCODE[Dns.RCODE.BADTIME] = "BADTIME"
+_RCODE[Dns.RCODE.BADMODE] = "BADMODE"
+_RCODE[Dns.RCODE.BADNAME] = "BADNAME"
+_RCODE[Dns.RCODE.BADALG] = "BADALG"
+_RCODE[Dns.RCODE.BADTRUNC] = "BADTRUNC"
+_RCODE[Dns.RCODE.BADCOOKIE] = "BADCOOKIE"
+local _AFSDB = {}
+_AFSDB[Dns.AFSDB.SUBTYPE_AFS3LOCSRV] = "SUBTYPE_AFS3LOCSRV"
+_AFSDB[Dns.AFSDB.SUBTYPE_DCENCA_ROOT] = "SUBTYPE_DCENCA_ROOT"
+local _DHCID = {}
+_DHCID[Dns.DHCID.TYPE_1OCTET] = "TYPE_1OCTET"
+_DHCID[Dns.DHCID.TYPE_DATAOCTET] = "TYPE_DATAOCTET"
+_DHCID[Dns.DHCID.TYPE_CLIENT_DUID] = "TYPE_CLIENT_DUID"
+local _EDNS0 = {}
+_EDNS0[Dns.EDNS0.OPT_LLQ] = "OPT_LLQ"
+_EDNS0[Dns.EDNS0.OPT_UL] = "OPT_UL"
+_EDNS0[Dns.EDNS0.OPT_NSID] = "OPT_NSID"
+_EDNS0[Dns.EDNS0.OPT_DAU] = "OPT_DAU"
+_EDNS0[Dns.EDNS0.OPT_DHU] = "OPT_DHU"
+_EDNS0[Dns.EDNS0.OPT_N3U] = "OPT_N3U"
+_EDNS0[Dns.EDNS0.OPT_CLIENT_SUBNET] = "OPT_CLIENT_SUBNET"
+_EDNS0[Dns.EDNS0.OPT_EXPIRE] = "OPT_EXPIRE"
+_EDNS0[Dns.EDNS0.OPT_COOKIE] = "OPT_COOKIE"
+_EDNS0[Dns.EDNS0.OPT_TCP_KEEPALIVE] = "OPT_TCP_KEEPALIVE"
+_EDNS0[Dns.EDNS0.OPT_PADDING] = "OPT_PADDING"
+_EDNS0[Dns.EDNS0.OPT_CHAIN] = "OPT_CHAIN"
+_EDNS0[Dns.EDNS0.OPT_DEVICEID] = "OPT_DEVICEID"
+Dns.CLASS_STR = _CLASS
+Dns.TYPE_STR = _TYPE
+Dns.OPCODE_STR = _OPCODE
+Dns.RCODE_STR = _RCODE
+Dns.AFSDB_STR = _AFSDB
+Dns.DHCID_STR = _DHCID
+Dns.EDNS0_STR = _EDNS0
+
+-- Create a new DNS object, optionally on-top of another object.
+function Dns.new(obj)
+ local self = C.core_object_dns_new()
+ self.obj_prev = obj
+ ffi.gc(self, C.core_object_dns_free)
+ return self
+end
+
+-- Return the textual type of the object.
+function Dns:type()
+ return "dns"
+end
+
+-- Return the previous object.
+function Dns:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Dns:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Dns:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Dns:copy()
+ return C.core_object_dns_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Dns:free()
+ C.core_object_dns_free(self)
+end
+
+-- Return the Log object to control logging of this module.
+function Dns:log()
+ return C.core_object_dns_log()
+end
+
+-- Begin parsing the underlaying object, first the header is parsed then
+-- optionally continue calling
+-- .IR parse_q ()
+-- for the number of questions (see
+-- .IR qdcount ).
+-- After that continue calling
+-- .IR parse_rr ()
+-- for the number of answers, authorities and additionals resource records
+-- (see
+-- .IR ancount ", "
+-- .I nscount
+-- and
+-- .IR arcount ).
+-- Returns 0 on success or negative integer on error which can be for
+-- malformed or truncated DNS (-2) or if more space for labels is needed (-3).
+function Dns:parse_header()
+ return C.core_object_dns_parse_header(self)
+end
+
+-- Parse the next resource record as a question.
+-- Returns 0 on success or negative integer on error which can be for
+-- malformed or truncated DNS (-2) or if more space for labels is needed (-3).
+function Dns:parse_q(q, labels, num_labels)
+ return C.core_object_dns_parse_q(self, q, labels, num_labels)
+end
+
+-- Parse the next resource record.
+-- Returns 0 on success or negative integer on error which can be for
+-- malformed or truncated DNS (-2) or if more space for labels is needed (-3).
+function Dns:parse_rr(rr, labels, num_labels)
+ return C.core_object_dns_parse_rr(self, rr, labels, num_labels)
+end
+
+-- Begin parsing the underlaying object using
+-- .IR parse_header "(), "
+-- .IR parse_q ()
+-- and
+-- .IR parse_rr ().
+-- The optional
+-- .I num_labels
+-- can be used to set a specific number of labels used for each question
+-- and resource record (default 16).
+-- Returns result code, an array of questions, an array of question labels,
+-- an array of resource records and an array of resource records labels.
+-- Result code is 0 on success or negative integer on error which can be for
+-- malformed or truncated DNS (-2) or if more space for labels is needed (-3).
+function Dns:parse(num_labels)
+ local qs, qls, rrs, rrls = {}, {}, {}, {}
+ if num_labels == nil then
+ num_labels = 16
+ end
+
+ ret = self:parse_header()
+ if ret ~= 0 then
+ return ret, qs, qls, rrs, rrls
+ end
+ for n = 1, self.qdcount do
+ local labels = label.new(num_labels)
+ local q = Q.new()
+ local ret = C.core_object_dns_parse_q(self, q, labels, num_labels)
+
+ if ret ~= 0 then
+ return ret, qs, qls, rrs, rrls
+ end
+ table.insert(qs, q)
+ table.insert(qls, labels)
+ end
+ for n = 1, self.ancount do
+ local labels = label.new(num_labels)
+ local rr = RR.new()
+ local ret = C.core_object_dns_parse_rr(self, rr, labels, num_labels)
+
+ if ret ~= 0 then
+ return ret, qs, qls, rrs, rrls
+ end
+ table.insert(rrs, rr)
+ table.insert(rrls, labels)
+ end
+ for n = 1, self.nscount do
+ local labels = label.new(num_labels)
+ local rr = RR.new()
+ local ret = C.core_object_dns_parse_rr(self, rr, labels, num_labels)
+
+ if ret ~= 0 then
+ return ret, qs, qls, rrs, rrls
+ end
+ table.insert(rrs, rr)
+ table.insert(rrls, labels)
+ end
+ for n = 1, self.arcount do
+ local labels = label.new(num_labels)
+ local rr = RR.new()
+ local ret = C.core_object_dns_parse_rr(self, rr, labels, num_labels)
+
+ if ret ~= 0 then
+ return ret, qs, qls, rrs, rrls
+ end
+ table.insert(rrs, rr)
+ table.insert(rrls, labels)
+ end
+
+ return 0, qs, qls, rrs, rrls
+end
+
+-- Begin parsing the underlaying object using
+-- .IR parse_header "(), "
+-- .IR parse_q ()
+-- and
+-- .IR parse_rr (),
+-- and print it's content.
+-- The optional
+-- .I num_labels
+-- can be used to set a specific number of labels used for each question
+-- and resource record (default 16).
+function Dns:print(num_labels)
+ if num_labels == nil then
+ num_labels = 16
+ end
+ local labels = label.new(num_labels)
+ local q = Q.new()
+ local rr = RR.new()
+
+ if self:parse_header() ~= 0 then
+ return
+ end
+
+ local flags = {}
+ if self.have_aa and self.aa == 1 then
+ table.insert(flags, "AA")
+ end
+ if self.have_tc and self.tc == 1 then
+ table.insert(flags, "TC")
+ end
+ if self.have_rd and self.rd == 1 then
+ table.insert(flags, "RD")
+ end
+ if self.have_ra and self.ra == 1 then
+ table.insert(flags, "RA")
+ end
+ if self.have_z and self.z == 1 then
+ table.insert(flags, "Z")
+ end
+ if self.have_ad and self.ad == 1 then
+ table.insert(flags, "AD")
+ end
+ if self.have_cd and self.cd == 1 then
+ table.insert(flags, "CD")
+ end
+
+ print("id:", self.id)
+ print("", "qr:", self.qr)
+ print("", "opcode:", Dns.opcode_tostring(self.opcode))
+ print("", "flags:", table.concat(flags, " "))
+ print("", "rcode:", Dns.rcode_tostring(self.rcode))
+ print("", "qdcount:", self.qdcount)
+ print("", "ancount:", self.ancount)
+ print("", "nscount:", self.nscount)
+ print("", "arcount:", self.arcount)
+
+ if self.qdcount > 0 then
+ print("questions:", "class", "type", "labels")
+ for n = 1, self.qdcount do
+ if C.core_object_dns_parse_q(self, q, labels, num_labels) ~= 0 then
+ return
+ end
+ print("", Dns.class_tostring(q.class), Dns.type_tostring(q.type), label.tooffstr(self, labels, num_labels))
+ end
+ end
+ if self.ancount > 0 then
+ print("answers:", "class", "type", "ttl", "labels", "RR labels")
+ for n = 1, self.ancount do
+ if C.core_object_dns_parse_rr(self, rr, labels, num_labels) ~= 0 then
+ return
+ end
+ if rr.rdata_labels == 0 then
+ print("", Dns.class_tostring(rr.class), Dns.type_tostring(rr.type), rr.ttl, label.tooffstr(self, labels, rr.labels))
+ else
+ print("", Dns.class_tostring(rr.class), Dns.type_tostring(rr.type), rr.ttl, label.tooffstr(self, labels, rr.labels), label.tooffstr(self, labels, rr.rdata_labels, rr.labels))
+ end
+ end
+ end
+ if self.nscount > 0 then
+ print("authorities:", "class", "type", "ttl", "labels", "RR labels")
+ for n = 1, self.nscount do
+ if C.core_object_dns_parse_rr(self, rr, labels, num_labels) ~= 0 then
+ return
+ end
+ if rr.rdata_labels == 0 then
+ print("", Dns.class_tostring(rr.class), Dns.type_tostring(rr.type), rr.ttl, label.tooffstr(self, labels, rr.labels))
+ else
+ print("", Dns.class_tostring(rr.class), Dns.type_tostring(rr.type), rr.ttl, label.tooffstr(self, labels, rr.labels), label.tooffstr(self, labels, rr.rdata_labels, rr.labels))
+ end
+ end
+ end
+ if self.arcount > 0 then
+ print("additionals:", "class", "type", "ttl", "labels", "RR labels")
+ for n = 1, self.arcount do
+ if C.core_object_dns_parse_rr(self, rr, labels, num_labels) ~= 0 then
+ return
+ end
+ if rr.rdata_labels == 0 then
+ print("", Dns.class_tostring(rr.class), Dns.type_tostring(rr.type), rr.ttl, label.tooffstr(self, labels, rr.labels))
+ else
+ print("", Dns.class_tostring(rr.class), Dns.type_tostring(rr.type), rr.ttl, label.tooffstr(self, labels, rr.labels), label.tooffstr(self, labels, rr.rdata_labels, rr.labels))
+ end
+ end
+ end
+end
+
+-- Return the textual name for a class.
+function Dns.class_tostring(class)
+ if Dns.CLASS_STR[class] == nil then
+ return "UNKNOWN("..class..")"
+ end
+ return Dns.CLASS_STR[class]
+end
+
+-- Return the textual name for a type.
+function Dns.type_tostring(type)
+ if Dns.TYPE_STR[type] == nil then
+ return "UNKNOWN("..type..")"
+ end
+ return Dns.TYPE_STR[type]
+end
+
+-- Return the textual name for an opcode.
+function Dns.opcode_tostring(opcode)
+ if Dns.OPCODE_STR[opcode] == nil then
+ return "UNKNOWN("..opcode..")"
+ end
+ return Dns.OPCODE_STR[opcode]
+end
+
+-- Return the textual name for a rcode.
+function Dns.rcode_tostring(rcode)
+ if Dns.RCODE_STR[rcode] == nil then
+ return "UNKNOWN("..rcode..")"
+ end
+ return Dns.RCODE_STR[rcode]
+end
+
+-- Return the textual name for an afsdb subtype.
+function Dns.afsdb_tostring(afsdb)
+ if Dns.AFSDB_STR[afsdb] == nil then
+ return "UNKNOWN("..afsdb..")"
+ end
+ return Dns.AFSDB_STR[afsdb]
+end
+
+-- Return the textual name for a dhcid type.
+function Dns.dhcid_tostring(dhcid)
+ if Dns.DHCID_STR[dhcid] == nil then
+ return "UNKNOWN("..dhcid..")"
+ end
+ return Dns.DHCID_STR[dhcid]
+end
+
+-- Return the textual name for an EDNS0 OPT record.
+function Dns.edns0_tostring(edns0)
+ if Dns.EDNS0_STR[edns0] == nil then
+ return "UNKNOWN("..edns0..")"
+ end
+ return Dns.EDNS0_STR[edns0]
+end
+
+core_object_dns_t = ffi.metatype(t_name, { __index = Dns })
+
+-- dnsjit.core.object (3),
+-- dnsjit.core.object.payload (3),
+-- dnsjit.core.object.dns.label (3),
+-- dnsjit.core.object.dns.q (3),
+-- dnsjit.core.object.dns.rr (3)
+return Dns
diff --git a/include/dnsjit/core/object/dns/label.lua b/include/dnsjit/core/object/dns/label.lua
new file mode 100644
index 0000000..bc78758
--- /dev/null
+++ b/include/dnsjit/core/object/dns/label.lua
@@ -0,0 +1,118 @@
+-- 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.core.object.dns.label
+-- Container of a DNS label
+--
+-- The object that describes a DNS label.
+-- To extract a domain name label first check that
+-- .I have_dn
+-- is set, then use
+-- .I "offset + 1"
+-- to indicate where in the payload the label start and
+-- .I length
+-- for how many bytes long it is.
+-- .SS Attributes
+-- .TP
+-- is_end
+-- .TP
+-- have_length
+-- Set if there is a length.
+-- .TP
+-- have_offset
+-- Set if there is an offset.
+-- .TP
+-- have_extension_bits
+-- Set if there is extension bits.
+-- .TP
+-- have_dn
+-- Set if the label contained a domain name.
+-- .TP
+-- extension_bits
+-- The extension bits.
+-- .TP
+-- length
+-- The length of the domain name.
+-- .TP
+-- offset
+-- If
+-- .I have_dn
+-- is set then this contains the offset within the payload to where this label
+-- start otherwise it contains the offset to another label.
+module(...,package.seeall)
+
+require("dnsjit.core.object.dns_h")
+local ffi = require("ffi")
+
+local Label = {}
+
+-- Create a new array of labels.
+function Label.new(size)
+ return ffi.new("core_object_dns_label_t[?]", size)
+end
+
+-- Returns labels as a string and an offset to the next label.
+-- The string may be nil if the first label was an offset.
+-- The offset may be nil if the last label was an extension bits or end marker.
+function Label.tostring(dns, labels, num_labels, offset_labels)
+ if offset_labels == nil then
+ offset_labels = 0
+ end
+ local dn
+ for n = 1, tonumber(num_labels) do
+ local label = labels[n - 1 + offset_labels]
+
+ if label.have_dn == 1 then
+ if dn == nil then
+ dn = ""
+ end
+ dn = dn .. ffi.string(dns.payload + label.offset + 1, label.length) .. "."
+ elseif label.have_offset == 1 then
+ return dn, label.offset
+ else
+ return dn, nil
+ end
+ end
+ return dn, nil
+end
+
+-- Returns labels as a string which also includes a textual notation of the
+-- offset in the form of
+-- .IR "<offset>label" .
+function Label.tooffstr(dns, labels, num_labels, offset_labels)
+ if offset_labels == nil then
+ offset_labels = 0
+ end
+ local dn = ""
+ for n = 1, tonumber(num_labels) do
+ local label = labels[n - 1 + offset_labels]
+
+ if label.have_dn == 1 then
+ dn = dn .. "<" .. tonumber(label.offset) .. ">" .. ffi.string(dns.payload + label.offset + 1, label.length) .. "."
+ elseif label.have_offset == 1 then
+ dn = dn .. "<" .. tonumber(label.offset) .. ">"
+ break
+ else
+ break
+ end
+ end
+ return dn
+end
+
+-- dnsjit.core.object.dns (3)
+return Label
diff --git a/include/dnsjit/core/object/dns/q.lua b/include/dnsjit/core/object/dns/q.lua
new file mode 100644
index 0000000..f65d9e8
--- /dev/null
+++ b/include/dnsjit/core/object/dns/q.lua
@@ -0,0 +1,52 @@
+-- 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.core.object.dns.q
+-- Container of a DNS question
+--
+-- The object that describes a DNS question.
+-- .SS Attributes
+-- .TP
+-- have_type
+-- Set if there is a type.
+-- .TP
+-- have_class
+-- Set if there is a class.
+-- .TP
+-- type
+-- The type.
+-- .TP
+-- class
+-- The class.
+-- .TP
+-- labels
+-- The number of labels found in the question.
+module(...,package.seeall)
+
+require("dnsjit.core.object.dns_h")
+local ffi = require("ffi")
+
+local Q = {}
+
+-- Create a new question.
+function Q.new(size)
+ return ffi.new("core_object_dns_q_t")
+end
+
+-- dnsjit.core.object.dns (3)
+return Q
diff --git a/include/dnsjit/core/object/dns/rr.lua b/include/dnsjit/core/object/dns/rr.lua
new file mode 100644
index 0000000..d360d96
--- /dev/null
+++ b/include/dnsjit/core/object/dns/rr.lua
@@ -0,0 +1,85 @@
+-- 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.core.object.dns.rr
+-- Container of a DNS resource record
+--
+-- The object that describes a DNS resource record.
+-- .SS Attributes
+-- .TP
+-- have_type
+-- Set if there is a type.
+-- .TP
+-- have_class
+-- Set if there is a class.
+-- .TP
+-- have_ttl
+-- Set if there is a ttl.
+-- .TP
+-- have_rdlength
+-- Set if there is a rdlength.
+-- .TP
+-- have_rdata
+-- Set if there is resource record data.
+-- .TP
+-- have_rdata_labels
+-- Set if there are any labels within the rdata.
+-- .TP
+-- have_padding
+-- Set if there is padding.
+-- .TP
+-- type
+-- The type.
+-- .TP
+-- class
+-- The class.
+-- .TP
+-- ttl
+-- The TTL.
+-- .TP
+-- rdlength
+-- The resource record data length.
+-- .TP
+-- labels
+-- The number of labels found in the record.
+-- .TP
+-- rdata_offset
+-- The offset within the payload for the resource record data.
+-- .TP
+-- rdata_labels
+-- The number of labels found inside the resource record data.
+-- .TP
+-- padding_offset
+-- The offset within the payload where the padding starts.
+-- .TP
+-- padding_length
+-- The length of the padding.
+module(...,package.seeall)
+
+require("dnsjit.core.object.dns_h")
+local ffi = require("ffi")
+
+local Rr = {}
+
+-- Create a new resource record.
+function Rr.new()
+ return ffi.new("core_object_dns_rr_t")
+end
+
+-- dnsjit.core.object.dns (3)
+return Rr
diff --git a/include/dnsjit/core/object/ether.c b/include/dnsjit/core/object/ether.c
new file mode 100644
index 0000000..dd7cd98
--- /dev/null
+++ b/include/dnsjit/core/object/ether.c
@@ -0,0 +1,45 @@
+/*
+ * 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 "core/object/ether.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_ether_t* core_object_ether_copy(const core_object_ether_t* self)
+{
+ core_object_ether_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_ether_t)));
+ memcpy(copy, self, sizeof(core_object_ether_t));
+ copy->obj_prev = 0;
+
+ return copy;
+}
+
+void core_object_ether_free(core_object_ether_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/ether.h b/include/dnsjit/core/object/ether.h
new file mode 100644
index 0000000..0fc9e35
--- /dev/null
+++ b/include/dnsjit/core/object/ether.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_ether_h
+#define __dnsjit_core_object_ether_h
+
+#include <stddef.h>
+
+#include <dnsjit/core/object/ether.hh>
+
+#define CORE_OBJECT_ETHER_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_ETHER, prev) \
+ , \
+ { 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0 }, 0 \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/ether.hh b/include/dnsjit/core/object/ether.hh
new file mode 100644
index 0000000..3b7a0cc
--- /dev/null
+++ b/include/dnsjit/core/object/ether.hh
@@ -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/>.
+ */
+
+//lua:require("dnsjit.core.object_h")
+
+typedef struct core_object_ether {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ uint8_t dhost[6];
+ uint8_t shost[6];
+ uint16_t type;
+} core_object_ether_t;
+
+core_object_ether_t* core_object_ether_copy(const core_object_ether_t* self);
+void core_object_ether_free(core_object_ether_t* self);
diff --git a/include/dnsjit/core/object/ether.lua b/include/dnsjit/core/object/ether.lua
new file mode 100644
index 0000000..3359d1f
--- /dev/null
+++ b/include/dnsjit/core/object/ether.lua
@@ -0,0 +1,78 @@
+-- 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.core.object.ether
+-- Ether part of a packet
+--
+-- The ether part of a packet that usually can be found in the object chain
+-- after parsing with, for example, Layer filter.
+-- .SS Attributes
+-- .TP
+-- dhost
+-- The destination ether address.
+-- .TP
+-- shost
+-- The source ether address.
+-- .TP
+-- type
+-- The packet type ID field / EtherType field.
+module(...,package.seeall)
+
+require("dnsjit.core.object.ether_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_ether_t"
+local core_object_ether_t
+local Ether = {}
+
+-- Return the textual type of the object.
+function Ether:type()
+ return "ether"
+end
+
+-- Return the previous object.
+function Ether:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Ether:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Ether:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Ether:copy()
+ return C.core_object_ether_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Ether:free()
+ C.core_object_ether_free(self)
+end
+
+core_object_ether_t = ffi.metatype(t_name, { __index = Ether })
+
+-- dnsjit.core.object (3),
+-- dnsjit.filter.layer (3)
+return Ether
diff --git a/include/dnsjit/core/object/gre.c b/include/dnsjit/core/object/gre.c
new file mode 100644
index 0000000..9a467c3
--- /dev/null
+++ b/include/dnsjit/core/object/gre.c
@@ -0,0 +1,45 @@
+/*
+ * 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 "core/object/gre.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_gre_t* core_object_gre_copy(const core_object_gre_t* self)
+{
+ core_object_gre_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_gre_t)));
+ memcpy(copy, self, sizeof(core_object_gre_t));
+ copy->obj_prev = 0;
+
+ return copy;
+}
+
+void core_object_gre_free(core_object_gre_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/gre.h b/include/dnsjit/core/object/gre.h
new file mode 100644
index 0000000..a716787
--- /dev/null
+++ b/include/dnsjit/core/object/gre.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_gre_h
+#define __dnsjit_core_object_gre_h
+
+#include <stddef.h>
+
+#include <dnsjit/core/object/gre.hh>
+
+#define CORE_OBJECT_GRE_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_GRE, prev) \
+ , \
+ 0, 0, 0, 0, 0 \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/gre.hh b/include/dnsjit/core/object/gre.hh
new file mode 100644
index 0000000..67a89b1
--- /dev/null
+++ b/include/dnsjit/core/object/gre.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.object_h")
+
+typedef struct core_object_gre {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ uint16_t gre_flags;
+ uint16_t ether_type;
+ uint16_t checksum;
+ uint16_t offset;
+ uint32_t key;
+ uint32_t sequence;
+ // TODO: routing list, check RFC 1701.
+} core_object_gre_t;
+
+core_object_gre_t* core_object_gre_copy(const core_object_gre_t* self);
+void core_object_gre_free(core_object_gre_t* self);
diff --git a/include/dnsjit/core/object/gre.lua b/include/dnsjit/core/object/gre.lua
new file mode 100644
index 0000000..703b02f
--- /dev/null
+++ b/include/dnsjit/core/object/gre.lua
@@ -0,0 +1,87 @@
+-- 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.core.object.gre
+-- Generic Routing Encapsulation (GRE) part of a packet
+--
+-- The GRE part of a packet that usually can be found in the object chain
+-- after parsing with, for example, Layer filter.
+-- See RFC 1701.
+-- .SS Attributes
+-- .TP
+-- gre_flags
+-- The GRE flags.
+-- .TP
+-- ether_type
+-- The protocol type of the payload packet.
+-- .TP
+-- checksum
+-- The checksum of the GRE header and the payload packet.
+-- .TP
+-- key
+-- The Key field contains a four octet number which was inserted by
+-- the encapsulator.
+-- .TP
+-- sequence
+-- The Sequence Number field contains an unsigned 32 bit integer which is
+-- inserted by the encapsulator.
+module(...,package.seeall)
+
+require("dnsjit.core.object.gre_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_gre_t"
+local core_object_gre_t
+local Gre = {}
+
+-- Return the textual type of the object.
+function Gre:type()
+ return "gre"
+end
+
+-- Return the previous object.
+function Gre:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Gre:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Gre:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Gre:copy()
+ return C.core_object_gre_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Gre:free()
+ C.core_object_gre_free(self)
+end
+
+core_object_gre_t = ffi.metatype(t_name, { __index = Gre })
+
+-- dnsjit.core.object (3),
+-- dnsjit.filter.layer (3)
+return Gre
diff --git a/include/dnsjit/core/object/icmp.c b/include/dnsjit/core/object/icmp.c
new file mode 100644
index 0000000..ebd13dc
--- /dev/null
+++ b/include/dnsjit/core/object/icmp.c
@@ -0,0 +1,45 @@
+/*
+ * 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 "core/object/icmp.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_icmp_t* core_object_icmp_copy(const core_object_icmp_t* self)
+{
+ core_object_icmp_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_icmp_t)));
+ memcpy(copy, self, sizeof(core_object_icmp_t));
+ copy->obj_prev = 0;
+
+ return copy;
+}
+
+void core_object_icmp_free(core_object_icmp_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/icmp.h b/include/dnsjit/core/object/icmp.h
new file mode 100644
index 0000000..65ffa7d
--- /dev/null
+++ b/include/dnsjit/core/object/icmp.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_icmp_h
+#define __dnsjit_core_object_icmp_h
+
+#include <stddef.h>
+
+#include <dnsjit/core/object/icmp.hh>
+
+#define CORE_OBJECT_ICMP_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_ICMP, prev) \
+ , \
+ 0, 0, 0 \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/icmp.hh b/include/dnsjit/core/object/icmp.hh
new file mode 100644
index 0000000..1a7aa88
--- /dev/null
+++ b/include/dnsjit/core/object/icmp.hh
@@ -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/>.
+ */
+
+//lua:require("dnsjit.core.object_h")
+
+typedef struct core_object_icmp {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ uint8_t type;
+ uint8_t code;
+ uint16_t cksum;
+} core_object_icmp_t;
+
+core_object_icmp_t* core_object_icmp_copy(const core_object_icmp_t* self);
+void core_object_icmp_free(core_object_icmp_t* self);
diff --git a/include/dnsjit/core/object/icmp.lua b/include/dnsjit/core/object/icmp.lua
new file mode 100644
index 0000000..52062ad
--- /dev/null
+++ b/include/dnsjit/core/object/icmp.lua
@@ -0,0 +1,78 @@
+-- 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.core.object.icmp
+-- An ICMP packet
+--
+-- An ICMP packet which is usually at the top of the object chain
+-- after parsing with, for example, Layer filter.
+-- .SS Attributes
+-- .TP
+-- type
+-- The type of ICMP message.
+-- .TP
+-- code
+-- The (response/error) code for the ICMP type message.
+-- .TP
+-- cksum
+-- The ICMP checksum.
+module(...,package.seeall)
+
+require("dnsjit.core.object.icmp_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_icmp_t"
+local core_object_icmp_t
+local Icmp = {}
+
+-- Return the textual type of the object.
+function Icmp:type()
+ return "icmp"
+end
+
+-- Return the previous object.
+function Icmp:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Icmp:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Icmp:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Icmp:copy()
+ return C.core_object_icmp_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Icmp:free()
+ C.core_object_icmp_free(self)
+end
+
+core_object_icmp_t = ffi.metatype(t_name, { __index = Icmp })
+
+-- dnsjit.core.object (3),
+-- dnsjit.filter.layer (3)
+return Icmp
diff --git a/include/dnsjit/core/object/icmp6.c b/include/dnsjit/core/object/icmp6.c
new file mode 100644
index 0000000..b79fb17
--- /dev/null
+++ b/include/dnsjit/core/object/icmp6.c
@@ -0,0 +1,45 @@
+/*
+ * 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 "core/object/icmp6.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_icmp6_t* core_object_icmp6_copy(const core_object_icmp6_t* self)
+{
+ core_object_icmp6_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_icmp6_t)));
+ memcpy(copy, self, sizeof(core_object_icmp6_t));
+ copy->obj_prev = 0;
+
+ return copy;
+}
+
+void core_object_icmp6_free(core_object_icmp6_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/icmp6.h b/include/dnsjit/core/object/icmp6.h
new file mode 100644
index 0000000..4586d33
--- /dev/null
+++ b/include/dnsjit/core/object/icmp6.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_icmp6_h
+#define __dnsjit_core_object_icmp6_h
+
+#include <stddef.h>
+
+#include <dnsjit/core/object/icmp6.hh>
+
+#define CORE_OBJECT_ICMP6_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_ICMP6, prev) \
+ , \
+ 0, 0, 0 \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/icmp6.hh b/include/dnsjit/core/object/icmp6.hh
new file mode 100644
index 0000000..2aa1ed4
--- /dev/null
+++ b/include/dnsjit/core/object/icmp6.hh
@@ -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/>.
+ */
+
+//lua:require("dnsjit.core.object_h")
+
+typedef struct core_object_icmp6 {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ uint8_t type;
+ uint8_t code;
+ uint16_t cksum;
+} core_object_icmp6_t;
+
+core_object_icmp6_t* core_object_icmp6_copy(const core_object_icmp6_t* self);
+void core_object_icmp6_free(core_object_icmp6_t* self);
diff --git a/include/dnsjit/core/object/icmp6.lua b/include/dnsjit/core/object/icmp6.lua
new file mode 100644
index 0000000..a3929bf
--- /dev/null
+++ b/include/dnsjit/core/object/icmp6.lua
@@ -0,0 +1,78 @@
+-- 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.core.object.icmp6
+-- An ICMPv6 packet
+--
+-- An ICMPv6 packet which is usually at the top of the object chain
+-- after parsing with, for example, Layer filter.
+-- .SS Attributes
+-- .TP
+-- type
+-- The type of ICMPv6 message.
+-- .TP
+-- code
+-- The (response/error) code for the ICMPv6 type message.
+-- .TP
+-- cksum
+-- The ICMPv6 checksum.
+module(...,package.seeall)
+
+require("dnsjit.core.object.icmp6_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_icmp6_t"
+local core_object_icmp6_t
+local Icmp6 = {}
+
+-- Return the textual type of the object.
+function Icmp6:type()
+ return "icmp6"
+end
+
+-- Return the previous object.
+function Icmp6:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Icmp6:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Icmp6:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Icmp6:copy()
+ return C.core_object_icmp6_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Icmp6:free()
+ C.core_object_icmp6_free(self)
+end
+
+core_object_icmp6_t = ffi.metatype(t_name, { __index = Icmp6 })
+
+-- dnsjit.core.object (3),
+-- dnsjit.filter.layer (3)
+return Icmp6
diff --git a/include/dnsjit/core/object/ieee802.c b/include/dnsjit/core/object/ieee802.c
new file mode 100644
index 0000000..80dd4ea
--- /dev/null
+++ b/include/dnsjit/core/object/ieee802.c
@@ -0,0 +1,45 @@
+/*
+ * 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 "core/object/ieee802.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_ieee802_t* core_object_ieee802_copy(const core_object_ieee802_t* self)
+{
+ core_object_ieee802_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_ieee802_t)));
+ memcpy(copy, self, sizeof(core_object_ieee802_t));
+ copy->obj_prev = 0;
+
+ return copy;
+}
+
+void core_object_ieee802_free(core_object_ieee802_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/ieee802.h b/include/dnsjit/core/object/ieee802.h
new file mode 100644
index 0000000..388430d
--- /dev/null
+++ b/include/dnsjit/core/object/ieee802.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_ieee802_h
+#define __dnsjit_core_object_ieee802_h
+
+#include <stddef.h>
+
+#include <dnsjit/core/object/ieee802.hh>
+
+#define CORE_OBJECT_IEEE802_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_IEEE802, prev) \
+ , \
+ 0, 0, 0, 0, 0 \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/ieee802.hh b/include/dnsjit/core/object/ieee802.hh
new file mode 100644
index 0000000..1bce670
--- /dev/null
+++ b/include/dnsjit/core/object/ieee802.hh
@@ -0,0 +1,35 @@
+/*
+ * 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.object_h")
+
+typedef struct core_object_ieee802 {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ uint16_t tpid;
+ uint8_t pcp;
+ uint8_t dei;
+ uint8_t vid;
+ uint16_t ether_type;
+} core_object_ieee802_t;
+
+core_object_ieee802_t* core_object_ieee802_copy(const core_object_ieee802_t* self);
+void core_object_ieee802_free(core_object_ieee802_t* self);
diff --git a/include/dnsjit/core/object/ieee802.lua b/include/dnsjit/core/object/ieee802.lua
new file mode 100644
index 0000000..77519ef
--- /dev/null
+++ b/include/dnsjit/core/object/ieee802.lua
@@ -0,0 +1,84 @@
+-- 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.core.object.ieee802
+-- IEEE802 part of a packet
+--
+-- The IEEE802 part of a packet that usually can be found in the object chain
+-- after parsing with, for example, Layer filter.
+-- .SS Attributes
+-- .TP
+-- tpid
+-- Tag protocol identifier.
+-- .TP
+-- pcp
+-- Priority code point.
+-- .TP
+-- dei
+-- Drop eligible indicator.
+-- .TP
+-- vid
+-- VLAN identifier.
+-- .TP
+-- ether_type
+-- The packet type ID field / EtherType field.
+module(...,package.seeall)
+
+require("dnsjit.core.object.ieee802_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_ieee802_t"
+local core_object_ieee802_t
+local Ieee802 = {}
+
+-- Return the textual type of the object.
+function Ieee802:type()
+ return "ieee802"
+end
+
+-- Return the previous object.
+function Ieee802:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Ieee802:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Ieee802:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Ieee802:copy()
+ return C.core_object_ieee802_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Ieee802:free()
+ C.core_object_ieee802_free(self)
+end
+
+core_object_ieee802_t = ffi.metatype(t_name, { __index = Ieee802 })
+
+-- dnsjit.core.object (3),
+-- dnsjit.filter.layer (3)
+return Ieee802
diff --git a/include/dnsjit/core/object/ip.c b/include/dnsjit/core/object/ip.c
new file mode 100644
index 0000000..4e50b60
--- /dev/null
+++ b/include/dnsjit/core/object/ip.c
@@ -0,0 +1,45 @@
+/*
+ * 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 "core/object/ip.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_ip_t* core_object_ip_copy(const core_object_ip_t* self)
+{
+ core_object_ip_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_ip_t)));
+ memcpy(copy, self, sizeof(core_object_ip_t));
+ copy->obj_prev = 0;
+
+ return copy;
+}
+
+void core_object_ip_free(core_object_ip_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/ip.h b/include/dnsjit/core/object/ip.h
new file mode 100644
index 0000000..00e927b
--- /dev/null
+++ b/include/dnsjit/core/object/ip.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_ip_h
+#define __dnsjit_core_object_ip_h
+
+#include <stddef.h>
+
+#include <dnsjit/core/object/ip.hh>
+
+#define CORE_OBJECT_IP_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_IP, prev) \
+ , \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/ip.hh b/include/dnsjit/core/object/ip.hh
new file mode 100644
index 0000000..9a379a4
--- /dev/null
+++ b/include/dnsjit/core/object/ip.hh
@@ -0,0 +1,40 @@
+/*
+ * 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.object_h")
+
+typedef struct core_object_ip {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ uint8_t v;
+ uint8_t hl;
+ uint8_t tos;
+ uint16_t len;
+ uint16_t id;
+ uint16_t off;
+ uint8_t ttl;
+ uint8_t p;
+ uint16_t sum;
+ uint8_t src[4], dst[4];
+} core_object_ip_t;
+
+core_object_ip_t* core_object_ip_copy(const core_object_ip_t* self);
+void core_object_ip_free(core_object_ip_t* self);
diff --git a/include/dnsjit/core/object/ip.lua b/include/dnsjit/core/object/ip.lua
new file mode 100644
index 0000000..0294612
--- /dev/null
+++ b/include/dnsjit/core/object/ip.lua
@@ -0,0 +1,122 @@
+-- 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.core.object.ip
+-- An IP packet
+--
+-- An IP packet that usually can be found in the object chain
+-- after parsing with, for example, Layer filter.
+-- .SS Attributes
+-- .TP
+-- v
+-- Version.
+-- .TP
+-- hl
+-- Header length.
+-- .TP
+-- tos
+-- Type of service.
+-- .TP
+-- len
+-- Total length.
+-- .TP
+-- id
+-- Identification.
+-- .TP
+-- off
+-- Fragment offset field.
+-- .TP
+-- ttl
+-- Time to live.
+-- .TP
+-- p
+-- Protocol.
+-- .TP
+-- sum
+-- Checksum.
+-- .TP
+-- src
+-- Source address.
+-- .TP
+-- dst
+-- Destination address.
+-- .TP
+-- payload
+-- A pointer to the payload.
+-- .TP
+-- plen
+-- The length of the payload.
+-- .TP
+-- pad_len
+-- The length of padding found, if any.
+module(...,package.seeall)
+
+require("dnsjit.core.object.ip_h")
+local ffi = require("ffi")
+local C = ffi.C
+local libip = require("dnsjit.lib.ip")
+
+local t_name = "core_object_ip_t"
+local core_object_ip_t
+local Ip = {}
+
+-- Return the textual type of the object.
+function Ip:type()
+ return "ip"
+end
+
+-- Return the previous object.
+function Ip:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Ip:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Ip:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Ip:copy()
+ return C.core_object_ip_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Ip:free()
+ C.core_object_ip_free(self)
+end
+
+-- Return the IP source as a string.
+function Ip:source()
+ return libip.ipstring(self.src)
+end
+
+-- Return the IP destination as a string.
+function Ip:destination()
+ return libip.ipstring(self.dst)
+end
+
+core_object_ip_t = ffi.metatype(t_name, { __index = Ip })
+
+-- dnsjit.core.object (3),
+-- dnsjit.filter.layer (3)
+return Ip
diff --git a/include/dnsjit/core/object/ip6.c b/include/dnsjit/core/object/ip6.c
new file mode 100644
index 0000000..45db361
--- /dev/null
+++ b/include/dnsjit/core/object/ip6.c
@@ -0,0 +1,45 @@
+/*
+ * 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 "core/object/ip6.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_ip6_t* core_object_ip6_copy(const core_object_ip6_t* self)
+{
+ core_object_ip6_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_ip6_t)));
+ memcpy(copy, self, sizeof(core_object_ip6_t));
+ copy->obj_prev = 0;
+
+ return copy;
+}
+
+void core_object_ip6_free(core_object_ip6_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/ip6.h b/include/dnsjit/core/object/ip6.h
new file mode 100644
index 0000000..660b9e9
--- /dev/null
+++ b/include/dnsjit/core/object/ip6.h
@@ -0,0 +1,42 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_ip6_h
+#define __dnsjit_core_object_ip6_h
+
+#include <stddef.h>
+
+#include <dnsjit/core/object/ip6.hh>
+
+#define CORE_OBJECT_IP6_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_IP6, prev) \
+ , \
+ 0, 0, 0, 0, \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
+ 0, 0, 0, 0, \
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/ip6.hh b/include/dnsjit/core/object/ip6.hh
new file mode 100644
index 0000000..5411dda
--- /dev/null
+++ b/include/dnsjit/core/object/ip6.hh
@@ -0,0 +1,42 @@
+/*
+ * 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.object_h")
+
+typedef struct core_object_ip6 {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ uint32_t flow;
+ uint16_t plen;
+ uint8_t nxt;
+ uint8_t hlim;
+ uint8_t src[16];
+ uint8_t dst[16];
+
+ uint8_t is_frag;
+ uint8_t have_rtdst;
+ uint16_t frag_offlg;
+ uint16_t frag_ident;
+ uint8_t rtdst[16];
+} core_object_ip6_t;
+
+core_object_ip6_t* core_object_ip6_copy(const core_object_ip6_t* self);
+void core_object_ip6_free(core_object_ip6_t* self);
diff --git a/include/dnsjit/core/object/ip6.lua b/include/dnsjit/core/object/ip6.lua
new file mode 100644
index 0000000..5271be3
--- /dev/null
+++ b/include/dnsjit/core/object/ip6.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.core.object.ip6
+-- An IPv6 packet
+--
+-- An IPv6 packet that usually can be found in the object chain
+-- after parsing with, for example, Layer filter.
+-- .SS Attributes
+-- .TP
+-- flow
+-- 4 bits version, 8 bits TC and 20 bits flow-ID.
+-- .TP
+-- plen
+-- Payload length (as in the IPv6 header).
+-- .TP
+-- nxt
+-- Next header.
+-- .TP
+-- hlim
+-- Hop limit.
+-- .TP
+-- src
+-- Source address.
+-- .TP
+-- dst
+-- Destination address.
+-- .TP
+-- is_frag
+-- 1 bit, set if packet is a fragment.
+-- .TP
+-- have_rtdst
+-- 1 bit, set if
+-- .I rtdst
+-- is set.
+-- .TP
+-- frag_offlg
+-- Offset, reserved, and flag taken from the fragment header.
+-- .TP
+-- frag_ident
+-- Identification taken from the fragment header.
+-- .TP
+-- rtdst
+-- Destination address found in the routing extension header.
+-- .TP
+-- payload
+-- A pointer to the payload.
+-- .TP
+-- len
+-- The length of the payload.
+-- .TP
+-- pad_len
+-- The length of padding found, if any.
+module(...,package.seeall)
+
+require("dnsjit.core.object.ip6_h")
+local ffi = require("ffi")
+local C = ffi.C
+local libip = require("dnsjit.lib.ip")
+
+local t_name = "core_object_ip6_t"
+local core_object_ip6_t
+local Ip6 = {}
+
+-- Return the textual type of the object.
+function Ip6:type()
+ return "ip6"
+end
+
+-- Return the previous object.
+function Ip6:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Ip6:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Ip6:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Ip6:copy()
+ return C.core_object_ip6_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Ip6:free()
+ C.core_object_ip6_free(self)
+end
+
+-- Return the IPv6 source as a string.
+-- If
+-- .I pretty
+-- is true then return an easier to read IPv6 address.
+function Ip6:source(pretty)
+ return libip.ip6string(self.src, pretty)
+end
+
+-- Return the IPv6 destination as a string.
+-- If
+-- .I pretty
+-- is true then return an easier to read IPv6 address.
+function Ip6:destination(pretty)
+ return libip.ip6string(self.dst, pretty)
+end
+
+core_object_ip6_t = ffi.metatype(t_name, { __index = Ip6 })
+
+-- dnsjit.core.object (3),
+-- dnsjit.filter.layer (3)
+return Ip6
diff --git a/include/dnsjit/core/object/linuxsll.c b/include/dnsjit/core/object/linuxsll.c
new file mode 100644
index 0000000..cacd23a
--- /dev/null
+++ b/include/dnsjit/core/object/linuxsll.c
@@ -0,0 +1,45 @@
+/*
+ * 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 "core/object/linuxsll.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_linuxsll_t* core_object_linuxsll_copy(const core_object_linuxsll_t* self)
+{
+ core_object_linuxsll_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_linuxsll_t)));
+ memcpy(copy, self, sizeof(core_object_linuxsll_t));
+ copy->obj_prev = 0;
+
+ return copy;
+}
+
+void core_object_linuxsll_free(core_object_linuxsll_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/linuxsll.h b/include/dnsjit/core/object/linuxsll.h
new file mode 100644
index 0000000..668581e
--- /dev/null
+++ b/include/dnsjit/core/object/linuxsll.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_linuxsll_h
+#define __dnsjit_core_object_linuxsll_h
+
+#include <stddef.h>
+
+#include <dnsjit/core/object/linuxsll.hh>
+
+#define CORE_OBJECT_LINUXSLL_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_LINUXSLL, prev) \
+ , \
+ 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0 \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/linuxsll.hh b/include/dnsjit/core/object/linuxsll.hh
new file mode 100644
index 0000000..e808f96
--- /dev/null
+++ b/include/dnsjit/core/object/linuxsll.hh
@@ -0,0 +1,35 @@
+/*
+ * 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.object_h")
+
+typedef struct core_object_linuxsll {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ uint16_t packet_type;
+ uint16_t arp_hardware;
+ uint16_t link_layer_address_length;
+ uint8_t link_layer_address[8];
+ uint16_t ether_type;
+} core_object_linuxsll_t;
+
+core_object_linuxsll_t* core_object_linuxsll_copy(const core_object_linuxsll_t* self);
+void core_object_linuxsll_free(core_object_linuxsll_t* self);
diff --git a/include/dnsjit/core/object/linuxsll.lua b/include/dnsjit/core/object/linuxsll.lua
new file mode 100644
index 0000000..6b47525
--- /dev/null
+++ b/include/dnsjit/core/object/linuxsll.lua
@@ -0,0 +1,84 @@
+-- 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.core.object.linuxsll
+-- Linux cooked-mode capture (SLL) part of a packet
+--
+-- The SLL part of a packet that usually can be found in the object chain
+-- after parsing with, for example, Layer filter.
+-- .SS Attributes
+-- .TP
+-- packet_type
+-- The packet type.
+-- .TP
+-- arp_hardware
+-- The link-layer device type.
+-- .TP
+-- link_layer_address_length
+-- The length of the link-layer address.
+-- .TP
+-- link_layer_address
+-- The link-layer address.
+-- .TP
+-- ether_type
+-- An Ethernet protocol type.
+module(...,package.seeall)
+
+require("dnsjit.core.object.linuxsll_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_linuxsll_t"
+local core_object_linuxsll_t
+local Linuxsll = {}
+
+-- Return the textual type of the object.
+function Linuxsll:type()
+ return "linuxsll"
+end
+
+-- Return the previous object.
+function Linuxsll:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Linuxsll:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Linuxsll:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Linuxsll:copy()
+ return C.core_object_linuxsll_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Linuxsll:free()
+ C.core_object_linuxsll_free(self)
+end
+
+core_object_linuxsll_t = ffi.metatype(t_name, { __index = Linuxsll })
+
+-- dnsjit.core.object (3).
+-- dnsjit.filter.layer (3)
+return Linuxsll
diff --git a/include/dnsjit/core/object/loop.c b/include/dnsjit/core/object/loop.c
new file mode 100644
index 0000000..4149f71
--- /dev/null
+++ b/include/dnsjit/core/object/loop.c
@@ -0,0 +1,45 @@
+/*
+ * 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 "core/object/loop.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_loop_t* core_object_loop_copy(const core_object_loop_t* self)
+{
+ core_object_loop_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_loop_t)));
+ memcpy(copy, self, sizeof(core_object_loop_t));
+ copy->obj_prev = 0;
+
+ return copy;
+}
+
+void core_object_loop_free(core_object_loop_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/loop.h b/include/dnsjit/core/object/loop.h
new file mode 100644
index 0000000..0cdb8e0
--- /dev/null
+++ b/include/dnsjit/core/object/loop.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_loop_h
+#define __dnsjit_core_object_loop_h
+
+#include <stddef.h>
+
+#include <dnsjit/core/object/loop.hh>
+
+#define CORE_OBJECT_LOOP_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_LOOP, prev) \
+ , \
+ 0 \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/loop.hh b/include/dnsjit/core/object/loop.hh
new file mode 100644
index 0000000..e8c5573
--- /dev/null
+++ b/include/dnsjit/core/object/loop.hh
@@ -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/>.
+ */
+
+//lua:require("dnsjit.core.object_h")
+
+typedef struct core_object_loop {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ uint32_t family;
+} core_object_loop_t;
+
+core_object_loop_t* core_object_loop_copy(const core_object_loop_t* self);
+void core_object_loop_free(core_object_loop_t* self);
diff --git a/include/dnsjit/core/object/loop.lua b/include/dnsjit/core/object/loop.lua
new file mode 100644
index 0000000..2cb441f
--- /dev/null
+++ b/include/dnsjit/core/object/loop.lua
@@ -0,0 +1,72 @@
+-- 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.core.object.loop
+-- OpenBSD loopback encapsulation (loop) part of a packet
+--
+-- The loop part of a packet that usually can be found in the object chain
+-- after parsing with, for example, Layer filter.
+-- .SS Attributes
+-- .TP
+-- family
+-- The link-layer header describing what type of packet is encapsulated.
+module(...,package.seeall)
+
+require("dnsjit.core.object.loop_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_loop_t"
+local core_object_loop_t
+local Loop = {}
+
+-- Return the textual type of the object.
+function Loop:type()
+ return "loop"
+end
+
+-- Return the previous object.
+function Loop:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Loop:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Loop:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Loop:copy()
+ return C.core_object_loop_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Loop:free()
+ C.core_object_loop_free(self)
+end
+
+core_object_loop_t = ffi.metatype(t_name, { __index = Loop })
+
+-- dnsjit.core.object (3),
+-- dnsjit.filter.layer (3)
+return Loop
diff --git a/include/dnsjit/core/object/null.c b/include/dnsjit/core/object/null.c
new file mode 100644
index 0000000..7fe617a
--- /dev/null
+++ b/include/dnsjit/core/object/null.c
@@ -0,0 +1,45 @@
+/*
+ * 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 "core/object/null.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_null_t* core_object_null_copy(const core_object_null_t* self)
+{
+ core_object_null_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_null_t)));
+ memcpy(copy, self, sizeof(core_object_null_t));
+ copy->obj_prev = 0;
+
+ return copy;
+}
+
+void core_object_null_free(core_object_null_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/null.h b/include/dnsjit/core/object/null.h
new file mode 100644
index 0000000..5127951
--- /dev/null
+++ b/include/dnsjit/core/object/null.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_null_h
+#define __dnsjit_core_object_null_h
+
+#include <stddef.h>
+
+#include <dnsjit/core/object/null.hh>
+
+#define CORE_OBJECT_NULL_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_NULL, prev) \
+ , \
+ 0 \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/null.hh b/include/dnsjit/core/object/null.hh
new file mode 100644
index 0000000..2e6c639
--- /dev/null
+++ b/include/dnsjit/core/object/null.hh
@@ -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/>.
+ */
+
+//lua:require("dnsjit.core.object_h")
+
+typedef struct core_object_null {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ uint32_t family;
+} core_object_null_t;
+
+core_object_null_t* core_object_null_copy(const core_object_null_t* self);
+void core_object_null_free(core_object_null_t* self);
diff --git a/include/dnsjit/core/object/null.lua b/include/dnsjit/core/object/null.lua
new file mode 100644
index 0000000..e6dfc5d
--- /dev/null
+++ b/include/dnsjit/core/object/null.lua
@@ -0,0 +1,72 @@
+-- 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.core.object.null
+-- BSD loopback encapsulation (null) part of a packet
+--
+-- The null part of a packet that usually can be found in the object chain
+-- after parsing with, for example, Layer filter.
+-- .SS Attributes
+-- .TP
+-- family
+-- The link-layer header describing what type of packet is encapsulated.
+module(...,package.seeall)
+
+require("dnsjit.core.object.null_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_null_t"
+local core_object_null_t
+local Null = {}
+
+-- Return the textual type of the object.
+function Null:type()
+ return "null"
+end
+
+-- Return the previous object.
+function Null:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Null:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Null:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Null:copy()
+ return C.core_object_null_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Null:free()
+ C.core_object_null_free(self)
+end
+
+core_object_null_t = ffi.metatype(t_name, { __index = Null })
+
+-- dnsjit.core.object (3),
+-- dnsjit.filter.layer (3)
+return Null
diff --git a/include/dnsjit/core/object/payload.c b/include/dnsjit/core/object/payload.c
new file mode 100644
index 0000000..d6d2ee4
--- /dev/null
+++ b/include/dnsjit/core/object/payload.c
@@ -0,0 +1,50 @@
+/*
+ * 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 "core/object/payload.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_payload_t* core_object_payload_copy(const core_object_payload_t* self)
+{
+ core_object_payload_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_payload_t) + self->len + self->padding));
+ memcpy(copy, self, sizeof(core_object_payload_t));
+ copy->obj_prev = 0;
+
+ if (copy->payload) {
+ copy->payload = (void*)copy + sizeof(core_object_payload_t);
+ memcpy((void*)copy->payload, self->payload, self->len + self->padding);
+ }
+
+ return copy;
+}
+
+void core_object_payload_free(core_object_payload_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/payload.h b/include/dnsjit/core/object/payload.h
new file mode 100644
index 0000000..07391aa
--- /dev/null
+++ b/include/dnsjit/core/object/payload.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_payload_h
+#define __dnsjit_core_object_payload_h
+
+#include <stddef.h>
+
+#include <dnsjit/core/object/payload.hh>
+
+#define CORE_OBJECT_PAYLOAD_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_PAYLOAD, prev) \
+ , \
+ 0, 0, 0 \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/payload.hh b/include/dnsjit/core/object/payload.hh
new file mode 100644
index 0000000..fc2ad71
--- /dev/null
+++ b/include/dnsjit/core/object/payload.hh
@@ -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/>.
+ */
+
+//lua:require("dnsjit.core.object_h")
+
+typedef struct core_object_payload {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ const uint8_t* payload;
+ size_t len, padding;
+} core_object_payload_t;
+
+core_object_payload_t* core_object_payload_copy(const core_object_payload_t* self);
+void core_object_payload_free(core_object_payload_t* self);
diff --git a/include/dnsjit/core/object/payload.lua b/include/dnsjit/core/object/payload.lua
new file mode 100644
index 0000000..61ea867
--- /dev/null
+++ b/include/dnsjit/core/object/payload.lua
@@ -0,0 +1,83 @@
+-- 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.core.object.payload
+-- Application data payload
+--
+-- Payload object contains the data carried by the underlying transport
+-- protocol.
+-- Payload is usually at the top of the object chain after parsing with,
+-- for example,
+-- .IR dnsjit.filter.layer .
+-- .SS Attributes
+-- .TP
+-- payload
+-- A pointer to the payload.
+-- .TP
+-- len
+-- The length of the payload.
+-- .TP
+-- padding
+-- The length of padding in the underlying packet.
+module(...,package.seeall)
+
+require("dnsjit.core.object.payload_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_payload_t"
+local core_object_payload_t
+local Payload = {}
+
+-- Return the textual type of the object.
+function Payload:type()
+ return "payload"
+end
+
+-- Return the previous object.
+function Payload:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Payload:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Payload:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Payload:copy()
+ return C.core_object_payload_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Payload:free()
+ C.core_object_payload_free(self)
+end
+
+core_object_payload_t = ffi.metatype(t_name, { __index = Payload })
+
+-- dnsjit.core.object (3),
+-- dnsjit.core.object.udp (3),
+-- dnsjit.core.object.tcp (3),
+-- dnsjit.filter.layer (3)
+return Payload
diff --git a/include/dnsjit/core/object/pcap.c b/include/dnsjit/core/object/pcap.c
new file mode 100644
index 0000000..576a94e
--- /dev/null
+++ b/include/dnsjit/core/object/pcap.c
@@ -0,0 +1,50 @@
+/*
+ * 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 "core/object/pcap.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_pcap_t* core_object_pcap_copy(const core_object_pcap_t* self)
+{
+ core_object_pcap_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_pcap_t) + self->caplen));
+ memcpy(copy, self, sizeof(core_object_pcap_t));
+ copy->obj_prev = 0;
+
+ if (copy->bytes) {
+ copy->bytes = (void*)copy + sizeof(core_object_pcap_t);
+ memcpy((void*)copy->bytes, self->bytes, self->caplen);
+ }
+
+ return copy;
+}
+
+void core_object_pcap_free(core_object_pcap_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/pcap.h b/include/dnsjit/core/object/pcap.h
new file mode 100644
index 0000000..afcccf3
--- /dev/null
+++ b/include/dnsjit/core/object/pcap.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_pcap_h
+#define __dnsjit_core_object_pcap_h
+
+#include <dnsjit/core/object/pcap.hh>
+
+#define CORE_OBJECT_PCAP_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_PCAP, prev) \
+ , \
+ 0, 0, \
+ { 0, 0 }, 0, 0, 0, \
+ 0 \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/pcap.hh b/include/dnsjit/core/object/pcap.hh
new file mode 100644
index 0000000..642a7e7
--- /dev/null
+++ b/include/dnsjit/core/object/pcap.hh
@@ -0,0 +1,38 @@
+/*
+ * 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.object_h")
+//lua:require("dnsjit.core.timespec_h")
+
+typedef struct core_object_pcap {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ uint32_t snaplen, linktype;
+
+ core_timespec_t ts;
+ uint32_t caplen, len;
+ const unsigned char* bytes;
+
+ uint8_t is_swapped;
+} core_object_pcap_t;
+
+core_object_pcap_t* core_object_pcap_copy(const core_object_pcap_t* self);
+void core_object_pcap_free(core_object_pcap_t* self);
diff --git a/include/dnsjit/core/object/pcap.lua b/include/dnsjit/core/object/pcap.lua
new file mode 100644
index 0000000..3d49326
--- /dev/null
+++ b/include/dnsjit/core/object/pcap.lua
@@ -0,0 +1,98 @@
+-- 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.core.object.pcap
+-- Container of a packet found in a PCAP
+--
+-- Container of a PCAP packet which contains information both from the PCAP
+-- itself and the
+-- .I pcap_pkthdr
+-- object receied for each packet in the PCAP.
+-- .SS Attributes
+-- .TP
+-- snaplen
+-- Max length saved portion of each packet.
+-- .TP
+-- linktype
+-- Data link type for the PCAP.
+-- .TP
+-- ts
+-- Time stamp of this packet.
+-- .TP
+-- caplen
+-- Length of portion present.
+-- .TP
+-- len
+-- Length of this packet (off wire).
+-- .TP
+-- bytes
+-- A pointer to the packet.
+-- .TP
+-- is_swapped
+-- Indicate if the byte order of the PCAP is different then the host.
+-- This is used in, for example, the Layer filter to correctly parse null
+-- objects since they are stored in the capturers host byte order.
+module(...,package.seeall)
+
+require("dnsjit.core.object.pcap_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_pcap_t"
+local core_object_pcap_t
+local Pcap = {}
+
+-- Return the textual type of the object.
+function Pcap:type()
+ return "pcap"
+end
+
+-- Return the previous object.
+function Pcap:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Pcap:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Pcap:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Pcap:copy()
+ return C.core_object_pcap_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Pcap:free()
+ C.core_object_pcap_free(self)
+end
+
+core_object_pcap_t = ffi.metatype(t_name, { __index = Pcap })
+
+-- dnsjit.core.object (3),
+-- dnsjit.input.pcap (3),
+-- dnsjit.input.fpcap (3),
+-- dnsjit.input.mmpcap (3),
+-- dnsjit.filter.layer (3),
+-- dnsjit.output.pcap (3)
+return Pcap
diff --git a/include/dnsjit/core/object/tcp.c b/include/dnsjit/core/object/tcp.c
new file mode 100644
index 0000000..37f9548
--- /dev/null
+++ b/include/dnsjit/core/object/tcp.c
@@ -0,0 +1,45 @@
+/*
+ * 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 "core/object/tcp.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_tcp_t* core_object_tcp_copy(const core_object_tcp_t* self)
+{
+ core_object_tcp_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_tcp_t)));
+ memcpy(copy, self, sizeof(core_object_tcp_t));
+ copy->obj_prev = 0;
+
+ return copy;
+}
+
+void core_object_tcp_free(core_object_tcp_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/tcp.h b/include/dnsjit/core/object/tcp.h
new file mode 100644
index 0000000..e851a8a
--- /dev/null
+++ b/include/dnsjit/core/object/tcp.h
@@ -0,0 +1,39 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_tcp_h
+#define __dnsjit_core_object_tcp_h
+
+#include <stddef.h>
+
+#include <dnsjit/core/object/tcp.hh>
+
+#define CORE_OBJECT_TCP_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_TCP, prev) \
+ , \
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
+ { 0 }, 0 \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/tcp.hh b/include/dnsjit/core/object/tcp.hh
new file mode 100644
index 0000000..bd3167d
--- /dev/null
+++ b/include/dnsjit/core/object/tcp.hh
@@ -0,0 +1,43 @@
+/*
+ * 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.object_h")
+
+typedef struct core_object_tcp {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ uint16_t sport;
+ uint16_t dport;
+ uint32_t seq;
+ uint32_t ack;
+ uint8_t off : 4;
+ uint8_t x2 : 4;
+ uint8_t flags;
+ uint16_t win;
+ uint16_t sum;
+ uint16_t urp;
+
+ uint8_t opts[64];
+ size_t opts_len;
+} core_object_tcp_t;
+
+core_object_tcp_t* core_object_tcp_copy(const core_object_tcp_t* self);
+void core_object_tcp_free(core_object_tcp_t* self);
diff --git a/include/dnsjit/core/object/tcp.lua b/include/dnsjit/core/object/tcp.lua
new file mode 100644
index 0000000..e1d6dc2
--- /dev/null
+++ b/include/dnsjit/core/object/tcp.lua
@@ -0,0 +1,110 @@
+-- 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.core.object.tcp
+-- A TCP segment header
+--
+-- A TCP segment header.
+-- The data itself is in the
+-- .I dnsjit.core.object.payload
+-- object, which is the next object in the chain after parsing with,
+-- for example,
+-- .IR dnsjit.filter.layer .
+-- .SS Attributes
+-- .TP
+-- sport
+-- Source port.
+-- .TP
+-- dport
+-- Destination port.
+-- .TP
+-- seq
+-- Sequence number.
+-- .TP
+-- ack
+-- Acknowledgement number.
+-- .TP
+-- off
+-- Data offset.
+-- .TP
+-- x2
+-- Unused.
+-- .TP
+-- flags
+-- TCP flags.
+-- .TP
+-- win
+-- Window.
+-- .TP
+-- sum
+-- Checksum.
+-- .TP
+-- urp
+-- Urgent pointer.
+-- .TP
+-- opts
+-- Array of bytes with the TCP options found.
+-- .TP
+-- opts_len
+-- Length of the TCP options.
+module(...,package.seeall)
+
+require("dnsjit.core.object.tcp_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_tcp_t"
+local core_object_tcp_t
+local Tcp = {}
+
+-- Return the textual type of the object.
+function Tcp:type()
+ return "tcp"
+end
+
+-- Return the previous object.
+function Tcp:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Tcp:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Tcp:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Tcp:copy()
+ return C.core_object_tcp_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Tcp:free()
+ C.core_object_tcp_free(self)
+end
+
+core_object_tcp_t = ffi.metatype(t_name, { __index = Tcp })
+
+-- dnsjit.core.object (3),
+-- dnsjit.core.object.payload (3),
+-- dnsjit.filter.layer (3)
+return Tcp
diff --git a/include/dnsjit/core/object/udp.c b/include/dnsjit/core/object/udp.c
new file mode 100644
index 0000000..3d05002
--- /dev/null
+++ b/include/dnsjit/core/object/udp.c
@@ -0,0 +1,45 @@
+/*
+ * 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 "core/object/udp.h"
+#include "core/assert.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+core_object_udp_t* core_object_udp_copy(const core_object_udp_t* self)
+{
+ core_object_udp_t* copy;
+ glassert_self();
+
+ glfatal_oom(copy = malloc(sizeof(core_object_udp_t)));
+ memcpy(copy, self, sizeof(core_object_udp_t));
+ copy->obj_prev = 0;
+
+ return copy;
+}
+
+void core_object_udp_free(core_object_udp_t* self)
+{
+ glassert_self();
+ free(self);
+}
diff --git a/include/dnsjit/core/object/udp.h b/include/dnsjit/core/object/udp.h
new file mode 100644
index 0000000..e85208b
--- /dev/null
+++ b/include/dnsjit/core/object/udp.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <dnsjit/core/object.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_core_object_udp_h
+#define __dnsjit_core_object_udp_h
+
+#include <stddef.h>
+
+#include <dnsjit/core/object/udp.hh>
+
+#define CORE_OBJECT_UDP_INIT(prev) \
+ { \
+ CORE_OBJECT_INIT(CORE_OBJECT_UDP, prev) \
+ , \
+ 0, 0, 0, 0, \
+ }
+
+#endif
diff --git a/include/dnsjit/core/object/udp.hh b/include/dnsjit/core/object/udp.hh
new file mode 100644
index 0000000..5efbd92
--- /dev/null
+++ b/include/dnsjit/core/object/udp.hh
@@ -0,0 +1,34 @@
+/*
+ * 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.object_h")
+
+typedef struct core_object_udp {
+ const core_object_t* obj_prev;
+ int32_t obj_type;
+
+ uint16_t sport;
+ uint16_t dport;
+ uint16_t ulen;
+ uint16_t sum;
+} core_object_udp_t;
+
+core_object_udp_t* core_object_udp_copy(const core_object_udp_t* self);
+void core_object_udp_free(core_object_udp_t* self);
diff --git a/include/dnsjit/core/object/udp.lua b/include/dnsjit/core/object/udp.lua
new file mode 100644
index 0000000..1652574
--- /dev/null
+++ b/include/dnsjit/core/object/udp.lua
@@ -0,0 +1,86 @@
+-- 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.core.object.udp
+-- A UDP datagram header
+--
+-- A UDP datagram header.
+-- The data itself is in the
+-- .I dnsjit.core.object.payload
+-- object, which is the next object in the chain after parsing with,
+-- for example,
+-- .IR dnsjit.filter.layer .
+-- .SS Attributes
+-- .TP
+-- sport
+-- Source port.
+-- .TP
+-- dport
+-- Destination port.
+-- .TP
+-- ulen
+-- UDP length (as described in the UDP header).
+-- .TP
+-- sum
+-- Checksum.
+module(...,package.seeall)
+
+require("dnsjit.core.object.udp_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_object_udp_t"
+local core_object_udp_t
+local Udp = {}
+
+-- Return the textual type of the object.
+function Udp:type()
+ return "udp"
+end
+
+-- Return the previous object.
+function Udp:prev()
+ return self.obj_prev
+end
+
+-- Cast the object to the underlining object module and return it.
+function Udp:cast()
+ return self
+end
+
+-- Cast the object to the generic object module and return it.
+function Udp:uncast()
+ return ffi.cast("core_object_t*", self)
+end
+
+-- Make a copy of the object and return it.
+function Udp:copy()
+ return C.core_object_udp_copy(self)
+end
+
+-- Free the object, should only be used on copies or otherwise allocated.
+function Udp:free()
+ C.core_object_udp_free(self)
+end
+
+core_object_udp_t = ffi.metatype(t_name, { __index = Udp })
+
+-- dnsjit.core.object (3),
+-- dnsjit.core.object.payload (3),
+-- dnsjit.filter.layer (3)
+return Udp
diff --git a/include/dnsjit/core/objects.lua b/include/dnsjit/core/objects.lua
new file mode 100644
index 0000000..ce83cd0
--- /dev/null
+++ b/include/dnsjit/core/objects.lua
@@ -0,0 +1,61 @@
+-- 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.core.objects
+-- Easy way to import all objects
+-- require("dnsjit.core.objects")
+--
+-- Helper module to require all available objects, returns what
+-- .I dnsjit.core.object
+-- returned so that constants like object types can be used.
+module(...,package.seeall)
+
+local object = require("dnsjit.core.object")
+require("dnsjit.core.object.pcap")
+require("dnsjit.core.object.ether")
+require("dnsjit.core.object.null")
+require("dnsjit.core.object.loop")
+require("dnsjit.core.object.linuxsll")
+require("dnsjit.core.object.ieee802")
+require("dnsjit.core.object.gre")
+require("dnsjit.core.object.ip")
+require("dnsjit.core.object.ip6")
+require("dnsjit.core.object.icmp")
+require("dnsjit.core.object.icmp6")
+require("dnsjit.core.object.udp")
+require("dnsjit.core.object.tcp")
+require("dnsjit.core.object.payload")
+require("dnsjit.core.object.dns")
+
+-- dnsjit.core.object (3),
+-- dnsjit.core.object.pcap (3),
+-- dnsjit.core.object.ether (3),
+-- dnsjit.core.object.null (3),
+-- dnsjit.core.object.loop (3),
+-- dnsjit.core.object.linuxsll (3),
+-- dnsjit.core.object.ieee802 (3),
+-- dnsjit.core.object.gre (3),
+-- dnsjit.core.object.ip (3),
+-- dnsjit.core.object.ip6 (3),
+-- dnsjit.core.object.icmp (3),
+-- dnsjit.core.object.icmp6 (3),
+-- dnsjit.core.object.udp (3),
+-- dnsjit.core.object.tcp (3),
+-- dnsjit.core.object.payload (3),
+-- dnsjit.core.object.dns (3)
+return object
diff --git a/include/dnsjit/core/producer.c b/include/dnsjit/core/producer.c
new file mode 100644
index 0000000..8a85c93
--- /dev/null
+++ b/include/dnsjit/core/producer.c
@@ -0,0 +1,23 @@
+/*
+ * 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 "core/producer.h"
diff --git a/include/dnsjit/core/producer.h b/include/dnsjit/core/producer.h
new file mode 100644
index 0000000..da63c17
--- /dev/null
+++ b/include/dnsjit/core/producer.h
@@ -0,0 +1,28 @@
+/*
+ * 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 <dnsjit/core/object.h>
+
+#ifndef __dnsjit_core_producer_h
+#define __dnsjit_core_producer_h
+
+#include <dnsjit/core/producer.hh>
+
+#endif
diff --git a/include/dnsjit/core/producer.hh b/include/dnsjit/core/producer.hh
new file mode 100644
index 0000000..2d64035
--- /dev/null
+++ b/include/dnsjit/core/producer.hh
@@ -0,0 +1,23 @@
+/*
+ * 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.object_h")
+
+typedef const core_object_t* (*core_producer_t)(void* ctx);
diff --git a/include/dnsjit/core/producer.lua b/include/dnsjit/core/producer.lua
new file mode 100644
index 0000000..42f3e44
--- /dev/null
+++ b/include/dnsjit/core/producer.lua
@@ -0,0 +1,28 @@
+-- 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.core.producer
+-- Producer interfaces
+-- require("dnsjit.core.producer_h")
+--
+-- Producer interfaces are used by input, filter and output modules to pass
+-- objects for processing.
+module(...,package.seeall)
+
+-- dnsjit.core.object (3)
+return
diff --git a/include/dnsjit/core/receiver.c b/include/dnsjit/core/receiver.c
new file mode 100644
index 0000000..f67be0e
--- /dev/null
+++ b/include/dnsjit/core/receiver.c
@@ -0,0 +1,23 @@
+/*
+ * 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 "core/receiver.h"
diff --git a/include/dnsjit/core/receiver.h b/include/dnsjit/core/receiver.h
new file mode 100644
index 0000000..5530b6a
--- /dev/null
+++ b/include/dnsjit/core/receiver.h
@@ -0,0 +1,28 @@
+/*
+ * 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 <dnsjit/core/object.h>
+
+#ifndef __dnsjit_core_receiver_h
+#define __dnsjit_core_receiver_h
+
+#include <dnsjit/core/receiver.hh>
+
+#endif
diff --git a/include/dnsjit/core/receiver.hh b/include/dnsjit/core/receiver.hh
new file mode 100644
index 0000000..316c9be
--- /dev/null
+++ b/include/dnsjit/core/receiver.hh
@@ -0,0 +1,23 @@
+/*
+ * 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.object_h")
+
+typedef void (*core_receiver_t)(void* ctx, const core_object_t* obj);
diff --git a/include/dnsjit/core/receiver.lua b/include/dnsjit/core/receiver.lua
new file mode 100644
index 0000000..02aa39d
--- /dev/null
+++ b/include/dnsjit/core/receiver.lua
@@ -0,0 +1,28 @@
+-- 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.core.receiver
+-- Receiver interfaces
+-- require("dnsjit.core.receiver_h")
+--
+-- Receiver interfaces are used by input, filter and output modules to pass
+-- objects for processing.
+module(...,package.seeall)
+
+-- dnsjit.core.object (3)
+return
diff --git a/include/dnsjit/core/thread.c b/include/dnsjit/core/thread.c
new file mode 100644
index 0000000..189c720
--- /dev/null
+++ b/include/dnsjit/core/thread.c
@@ -0,0 +1,229 @@
+/*
+ * 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 "globals.h"
+#include "core/assert.h"
+#include "core/thread.h"
+
+#include <string.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+static core_log_t _log = LOG_T_INIT("core.thread");
+static core_thread_t _defaults = {
+ LOG_T_INIT_OBJ("core.thread"),
+ 0, 0, 0, 0,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER,
+ 0, 0
+};
+
+core_log_t* core_thread_log()
+{
+ return &_log;
+}
+
+void core_thread_init(core_thread_t* self)
+{
+ mlassert_self();
+
+ *self = _defaults;
+}
+
+void core_thread_destroy(core_thread_t* self)
+{
+ core_thread_item_t* item;
+ mlassert_self();
+
+ free(self->bytecode);
+ while ((item = self->stack)) {
+ self->stack = item->next;
+ free(item);
+ }
+}
+
+static void* _thread(void* vp)
+{
+ core_thread_t* self = (core_thread_t*)vp;
+ lua_State* L;
+ mlassert_self();
+
+ // TODO: move to dnsjit_newstate()
+ L = luaL_newstate();
+ lassert(L, "could not create new Lua state");
+ luaL_openlibs(L);
+ dnsjit_globals(L);
+
+ for (;;) {
+ lua_getfield(L, LUA_GLOBALSINDEX, "require");
+ lua_pushstring(L, "dnsjit.core.thread");
+ if (lua_pcall(L, 1, 1, 0)) {
+ lcritical("%s", lua_tostring(L, -1));
+ break;
+ }
+ lua_getfield(L, -1, "_in_thread");
+ lua_pushlightuserdata(L, (void*)self);
+ lua_pushlstring(L, self->bytecode, self->bytecode_len);
+ if (lua_pcall(L, 2, 0, 0)) {
+ lcritical("%s", lua_tostring(L, -1));
+ }
+ break;
+ }
+
+ lua_close(L);
+ return 0;
+}
+
+int core_thread_start(core_thread_t* self, const char* bytecode, size_t len)
+{
+ int err;
+ mlassert_self();
+
+ if (self->bytecode) {
+ lfatal("bytecode already set");
+ }
+
+ lfatal_oom(self->bytecode = malloc(len));
+ memcpy(self->bytecode, bytecode, len);
+ self->bytecode_len = len;
+
+ if ((err = pthread_create(&self->thr_id, 0, _thread, (void*)self))) {
+ lcritical("pthread_create() error: %s", core_log_errstr(err));
+ return -1;
+ }
+
+ return 0;
+}
+
+int core_thread_stop(core_thread_t* self)
+{
+ int err;
+ mlassert_self();
+
+ if ((err = pthread_join(self->thr_id, 0))) {
+ lcritical("pthread_join() error: %s", core_log_errstr(err));
+ return -1;
+ }
+
+ return 0;
+}
+
+inline static void _push(core_thread_t* self, core_thread_item_t* item)
+{
+ if (pthread_mutex_lock(&self->lock)) {
+ lfatal("mutex lock failed");
+ }
+ if (!self->last) {
+ self->stack = self->last = item;
+ } else {
+ self->last->next = item;
+ self->last = item;
+ }
+ if (pthread_cond_signal(&self->cond)) {
+ lfatal("cond signal failed");
+ }
+ if (pthread_mutex_unlock(&self->lock)) {
+ lfatal("mutex unlock failed");
+ }
+}
+
+void core_thread_push(core_thread_t* self, void* ptr, const char* type, size_t type_len, const char* module, size_t module_len)
+{
+ core_thread_item_t* item;
+ mlassert_self();
+ lassert(ptr, "ptr is nil");
+ lassert(type, "type is nil");
+ lassert(type_len, "type_len is zero");
+ lassert(module, "module is nil");
+ lassert(module_len, "module_len is zero");
+
+ lfatal_oom(item = malloc(sizeof(core_thread_item_t) + type_len + module_len + 2));
+ item->next = 0;
+ item->ptr = ptr;
+ item->type = ((void*)item) + sizeof(core_thread_item_t);
+ memcpy(item->type, type, type_len);
+ item->type[type_len] = 0;
+ item->module = item->type + type_len + 1;
+ memcpy(item->module, module, module_len);
+ item->module[module_len] = 0;
+
+ _push(self, item);
+}
+
+void core_thread_push_string(core_thread_t* self, const char* str, size_t len)
+{
+ core_thread_item_t* item;
+ mlassert_self();
+ lassert(str, "str is nil");
+ lassert(len, "len is zero");
+
+ lfatal_oom(item = malloc(sizeof(core_thread_item_t) + len + 1));
+ item->next = 0;
+ item->ptr = 0;
+ item->str = ((void*)item) + sizeof(core_thread_item_t);
+ memcpy(item->str, str, len);
+ item->str[len] = 0;
+
+ _push(self, item);
+}
+
+void core_thread_push_int64(core_thread_t* self, int64_t i64)
+{
+ core_thread_item_t* item;
+ mlassert_self();
+
+ lfatal_oom(item = malloc(sizeof(core_thread_item_t)));
+ item->next = 0;
+ item->ptr = 0;
+ item->str = 0;
+ item->i64 = i64;
+
+ _push(self, item);
+}
+
+const core_thread_item_t* core_thread_pop(core_thread_t* self)
+{
+ mlassert_self();
+
+ if (pthread_mutex_lock(&self->lock)) {
+ lfatal("mutex lock failed");
+ }
+ if (!self->at) {
+ while (!self->stack) {
+ if (pthread_cond_wait(&self->cond, &self->lock)) {
+ lfatal("cond wait failed");
+ }
+ }
+ self->at = self->stack;
+ } else {
+ while (!self->at->next) {
+ if (pthread_cond_wait(&self->cond, &self->lock)) {
+ lfatal("cond wait failed");
+ }
+ }
+ self->at = self->at->next;
+ }
+ if (pthread_mutex_unlock(&self->lock)) {
+ lfatal("mutex unlock failed");
+ }
+
+ return self->at;
+}
diff --git a/include/dnsjit/core/thread.h b/include/dnsjit/core/thread.h
new file mode 100644
index 0000000..6b7ede6
--- /dev/null
+++ b/include/dnsjit/core/thread.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 <dnsjit/core/log.h>
+
+#ifndef __dnsjit_core_thread_h
+#define __dnsjit_core_thread_h
+
+#include <pthread.h>
+#include <unistd.h>
+
+#include <dnsjit/core/thread.hh>
+
+#endif
diff --git a/include/dnsjit/core/thread.hh b/include/dnsjit/core/thread.hh
new file mode 100644
index 0000000..a61f8c9
--- /dev/null
+++ b/include/dnsjit/core/thread.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/>.
+ */
+
+//lua:require("dnsjit.core.compat_h")
+//lua:require("dnsjit.core.log")
+
+typedef struct core_thread_item core_thread_item_t;
+struct core_thread_item {
+ core_thread_item_t* next;
+ void* ptr;
+ char * type, *module;
+
+ char* str;
+ int64_t i64;
+};
+
+typedef struct core_thread {
+ core_log_t _log;
+ pthread_t thr_id;
+ core_thread_item_t * stack, *last;
+ const core_thread_item_t* at;
+
+ pthread_mutex_t lock;
+ pthread_cond_t cond;
+
+ char* bytecode;
+ size_t bytecode_len;
+} core_thread_t;
+
+core_log_t* core_thread_log();
+
+void core_thread_init(core_thread_t* self);
+void core_thread_destroy(core_thread_t* self);
+int core_thread_start(core_thread_t* self, const char* bytecode, size_t len);
+int core_thread_stop(core_thread_t* self);
+void core_thread_push(core_thread_t* self, void* ptr, const char* type, size_t type_len, const char* module, size_t module_len);
+void core_thread_push_string(core_thread_t* self, const char* str, size_t len);
+void core_thread_push_int64(core_thread_t* self, int64_t i64);
+const core_thread_item_t* core_thread_pop(core_thread_t* self);
diff --git a/include/dnsjit/core/thread.lua b/include/dnsjit/core/thread.lua
new file mode 100644
index 0000000..f225726
--- /dev/null
+++ b/include/dnsjit/core/thread.lua
@@ -0,0 +1,141 @@
+-- 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.core.thread
+-- POSIX thread with separate Lua state
+-- local thr = require("dnsjit.core.thread").new()
+-- thr:start(function(thr)
+-- print("Hello from thread")
+-- print("got:", thr:pop(), " = ", thr:pop(3))
+-- end)
+-- thr:push("value from main", 1, 2, 3)
+-- thr:stop()
+--
+-- Start a new POSIX thread with it's own Lua state.
+-- Sharable objects can be passed to the thread by pushing and poping them of
+-- the thread stack.
+-- The Thread object and any other objects passed to the thread needs to be
+-- kept alive as long as the thread is running.
+module(...,package.seeall)
+
+require("dnsjit.core.thread_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "core_thread_t"
+local core_thread_t
+local Thread = {
+ _in_thread = function(thr, bytecode)
+ thr = ffi.cast("core_thread_t*", thr)
+ loadstring(bytecode)(thr)
+ end
+}
+
+-- Create a new Thread object.
+function Thread.new()
+ local self = core_thread_t()
+ C.core_thread_init(self)
+ ffi.gc(self, C.core_thread_destroy)
+ return self
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Thread:log()
+ if self == nil then
+ return C.core_thread_log()
+ end
+ return self._log
+end
+
+-- Start the thread and execute the given function in a separate Lua state,
+-- first argument to the function will be the Thread object that created it.
+-- Returns 0 on success.
+function Thread:start(func)
+ local bc = string.dump(func)
+ return C.core_thread_start(self, bc, #bc)
+end
+
+-- Wait for the thread to return.
+-- Returns 0 on success.
+function Thread:stop()
+ return C.core_thread_stop(self)
+end
+
+-- Push string(s), number(s) or sharable object(s) onto the thread stack so
+-- they can be retrieved inside the thread using
+-- .IR pop() .
+-- The sharable object(s) needs to be kept alive as long as the thread is
+-- running, strings and numbers are copied.
+function Thread:push(...)
+ for _, obj in pairs({...}) do
+ local t = type(obj)
+ if t == "string" then
+ C.core_thread_push_string(self, obj, #obj)
+ elseif t == "number" then
+ C.core_thread_push_int64(self, obj)
+ else
+ local ptr, type, module = obj:share()
+ C.core_thread_push(self, ptr, type, #type, module, #module)
+ end
+ end
+end
+
+-- Pop value(s) off the thread stack, should only be called within the thread.
+-- If
+-- .I num
+-- is not given then one value is poped.
+-- Returns nil if no values are left on the stack.
+function Thread:pop(num)
+ if num == nil or num == 1 then
+ local item = C.core_thread_pop(self)
+ if item == nil then
+ return
+ end
+ if item.ptr == nil then
+ if item.str == nil then
+ return tonumber(item.i64)
+ end
+ return ffi.string(item.str)
+ end
+ require(ffi.string(item.module))
+ return ffi.cast(ffi.string(item.type), item.ptr)
+ end
+
+ local ret = {}
+ for n = 1, num do
+ local item = C.core_thread_pop(self)
+ if item == nil then break end
+
+ if item.ptr == nil then
+ if item.str == nil then
+ table.insert(ret, tonumber(item.i64))
+ else
+ table.insert(ret, ffi.string(item.str))
+ end
+ else
+ require(ffi.string(item.module))
+ table.insert(ret, ffi.cast(ffi.string(item.type), item.ptr))
+ end
+ end
+ return unpack(ret)
+end
+
+core_thread_t = ffi.metatype(t_name, { __index = Thread })
+
+-- dnsjit.core.channel (3)
+return Thread
diff --git a/include/dnsjit/core/timespec.h b/include/dnsjit/core/timespec.h
new file mode 100644
index 0000000..2110c6d
--- /dev/null
+++ b/include/dnsjit/core/timespec.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/>.
+ */
+
+#ifndef __dnsjit_core_timespec_h
+#define __dnsjit_core_timespec_h
+
+#include <stdint.h>
+
+#include <dnsjit/core/timespec.hh>
+
+#define CORE_TIMESPEC_INIT \
+ { \
+ 0, 0 \
+ }
+
+#endif
diff --git a/include/dnsjit/core/timespec.hh b/include/dnsjit/core/timespec.hh
new file mode 100644
index 0000000..17a9551
--- /dev/null
+++ b/include/dnsjit/core/timespec.hh
@@ -0,0 +1,24 @@
+/*
+ * 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/>.
+ */
+
+typedef struct core_timespec {
+ int64_t sec;
+ int64_t nsec;
+} core_timespec_t;
diff --git a/include/dnsjit/core/timespec.lua b/include/dnsjit/core/timespec.lua
new file mode 100644
index 0000000..05c600f
--- /dev/null
+++ b/include/dnsjit/core/timespec.lua
@@ -0,0 +1,54 @@
+-- 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.core.receiver
+-- Non-system depended time specification structure definition
+-- typedef struct core_timespec {
+-- int64_t sec;
+-- int64_t nsec;
+-- } core_timespec_t;
+-- .SS C
+-- #include "core/timespec.h"
+-- .SS Lua
+-- require("dnsjit.core.timespec_h")
+-- .SS Lua functions
+-- local ts = require("dnsjit.core.timespec"):max_init()
+--
+-- Mainly used in C modules for a system independent time specification
+-- structure that can be passed to Lua.
+module(...,package.seeall)
+
+require("dnsjit.core.timespec_h")
+
+local ffi = require("ffi")
+
+local Timespec = {}
+
+-- Return a new structure with both
+-- .I sec
+-- and
+-- .I nsec
+-- set to 2LL ^ 62, the maximum positive values according to Lua.
+function Timespec:max_init()
+ local ts = ffi.new("core_timespec_t")
+ ts.sec = 2LL ^ 62
+ ts.nsec = 2LL ^ 62
+ return ts
+end
+
+return Timespec
diff --git a/include/dnsjit/dnsjit.1in b/include/dnsjit/dnsjit.1in
new file mode 100644
index 0000000..ae1ca93
--- /dev/null
+++ b/include/dnsjit/dnsjit.1in
@@ -0,0 +1,144 @@
+.\" 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/>.
+.\"
+.TH dnsjit 1 "@PACKAGE_VERSION@" "dnsjit"
+.SH NAME
+dnsjit \- DNS engine based around LuaJIT for capturing, parsing, replaying and statistics gathering
+.SH SYNOPSIS
+.SS Run a Lua script
+.B dnsjit
+.I file.lua
+.I ...
+.SS Shebang-style
+ #!/usr/bin/env dnsjit
+ ...
+.SH DESCRIPTION
+.B dnsjit
+is a combination of parts taken from
+.BR dsc ,
+.BR dnscap ,
+.BR drool,
+and put together around Lua to create a script-based engine for easy
+capturing, parsing and statistics gathering of DNS message while also
+providing facilities for replaying DNS traffic.
+.LP
+One of the core functionality that
+.B dnsjit
+brings is to tie together C and Lua modules through a receiver/receive
+interface.
+This allows creation of custom chains of functionality to meet various
+requirements.
+Another core functionality is the ability to parse and process DNS messages
+even if the messages are non-compliant with the DNS standards.
+.LP
+.B NOTE
+current implementation is
+.I ALPHA
+which means functionality are not set and may be changed or removed.
+.LP
+.SH MODULE CATEGORIES
+The following Lua module categories exists:
+.TP
+.B dnsjit.core
+Core modules for handling things like logging, DNS messages and
+receiver/receive functionality.
+.TP
+.B dnsjit.lib
+Various Lua libraries or C library bindings.
+.TP
+.B dnsjit.input
+Input modules used to read DNS messages in various ways.
+.TP
+.B dnsjit.filter
+Filter modules to process or manipulate DNS messages.
+.TP
+.B dnsjit.output
+Output modules used to display DNS message, export to various formats or
+replay them against other targets.
+.LP
+See each category's man-page for more information.
+.SH LUA GLOBALS
+The following Lua global variables are defined:
+.TP
+.B DNSJIT_VERSION
+A string with the full version.
+.TP
+.B DNSJIT_MAJOR_VERSION
+An integer with the major version number.
+.TP
+.B DNSJIT_MINOR_VERSION
+An integer with the minor version number.
+.TP
+.B DNSJIT_PATCH_VERSION
+An integer with the patch version number.
+.TP
+.B DNSJIT_BUGREPORT
+A string with the email address to file bug reports to.
+.TP
+.B DNSJIT_URL
+A string with the URL to the repository issue tracker, preferred place to
+file bug reports.
+.SH EXAMPLE
+Following example display the DNS ID found in queries.
+.LP
+ local input = require("dnsjit.input.pcapthread").new()
+ local output = require("dnsjit.filter.lua").new()
+
+ output:func(function(filter, object)
+ local packet = object:cast()
+ local dns = require("dnsjit.core.object.dns").new(packet)
+ dns:parse()
+ print(dns.id)
+ end)
+
+ input:open_offline("file.pcap")
+ input:only_queries(true)
+ input:receiver(output)
+ input:run()
+.LP
+See more examples in the
+.I examples
+directory.
+.SH SEE ALSO
+.BR dnsjit.core (3),
+.BR dnsjit.lib (3),
+.BR dnsjit.input (3),
+.BR dnsjit.filter (3),
+.BR dnsjit.output (3)
+.SH AUTHORS
+Jerry Lundström, DNS-OARC
+.LP
+Maintained by DNS-OARC
+.LP
+.RS
+.I https://www.dns-oarc.net/
+.RE
+.LP
+.SH BUGS
+For issues and feature requests please use:
+.LP
+.RS
+\fI@PACKAGE_URL@\fP
+.RE
+.LP
+For question and help please use:
+.LP
+.RS
+\fI@PACKAGE_BUGREPORT@\fP
+.RE
+.LP
diff --git a/include/dnsjit/dnsjit.c b/include/dnsjit/dnsjit.c
new file mode 100644
index 0000000..1024c4e
--- /dev/null
+++ b/include/dnsjit/dnsjit.c
@@ -0,0 +1,111 @@
+/*
+ * 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 "globals.h"
+#include "core/log.h"
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+
+static void* _sighthr(void* arg)
+{
+ sigset_t* set = (sigset_t*)arg;
+ int sig = 0, err;
+
+ if ((err = sigwait(set, &sig))) {
+ gldebug("sigwait %d", err);
+ }
+ glfatal("signal %d", sig);
+
+ return 0;
+}
+
+int main(int argc, char* argv[])
+{
+ lua_State* L;
+ int n, err;
+ sigset_t set;
+ pthread_t sighthr;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <file.lua> ...\n", argv[0]);
+ exit(1);
+ }
+
+ sigfillset(&set);
+ if ((err = pthread_sigmask(SIG_BLOCK, &set, 0))) {
+ glfatal("Unable to set blocked signals with pthread_sigmask()");
+ return 2;
+ }
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGTERM);
+ sigaddset(&set, SIGQUIT);
+ sigaddset(&set, SIGINT);
+
+ if ((err = pthread_create(&sighthr, 0, _sighthr, &set))) {
+ glfatal("Unable to start signal thread with pthread_create()");
+ return 2;
+ }
+
+ L = luaL_newstate();
+ luaL_openlibs(L);
+ dnsjit_globals(L);
+
+ lua_createtable(L, argc, 0);
+ for (n = 0; n < argc; n++) {
+ lua_pushstring(L, argv[n]);
+ lua_rawseti(L, -2, n);
+ }
+ lua_setglobal(L, "arg");
+ if ((err = luaL_loadfile(L, argv[1]))) {
+ switch (err) {
+ case LUA_ERRSYNTAX:
+ glcritical("%s: syntax error during pre-compilation", argv[1]);
+ break;
+ case LUA_ERRMEM:
+ glcritical("%s: memory allocation error", argv[1]);
+ break;
+ case LUA_ERRFILE:
+ glcritical("%s: cannot open/read file", argv[1]);
+ break;
+ default:
+ glcritical("%s: unknown error %d", argv[1], err);
+ break;
+ }
+ return 1;
+ }
+ if (lua_pcall(L, 0, 0, 0)) {
+ glcritical("%s: %s", argv[1], lua_tostring(L, -1));
+ lua_pop(L, 1);
+ return 1;
+ }
+ lua_close(L);
+
+ return 0;
+}
diff --git a/include/dnsjit/filter.lua b/include/dnsjit/filter.lua
new file mode 100644
index 0000000..fbb3736
--- /dev/null
+++ b/include/dnsjit/filter.lua
@@ -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/>.
+
+-- dnsjit.filter
+-- Filter/processing modules for dnsjit
+--
+-- Filter modules to process or manipulate objects, packets and/or DNS
+-- messages.
+module(...,package.seeall)
+
+-- dnsjit.filter.copy (3),
+-- dnsjit.filter.ipsplit (3),
+-- dnsjit.filter.layer (3),
+-- dnsjit.filter.split (3),
+-- dnsjit.filter.timing (3)
+return
diff --git a/include/dnsjit/filter/copy.c b/include/dnsjit/filter/copy.c
new file mode 100644
index 0000000..04a90ef
--- /dev/null
+++ b/include/dnsjit/filter/copy.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2019, CZ.NIC, z.s.p.o.
+ * 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 "filter/copy.h"
+#include "core/assert.h"
+
+static core_log_t _log = LOG_T_INIT("filter.copy");
+static filter_copy_t _defaults = {
+ LOG_T_INIT_OBJ("filter.copy"),
+ 0, 0,
+ 0
+};
+
+core_log_t* filter_copy_log()
+{
+ return &_log;
+}
+
+void filter_copy_init(filter_copy_t* self)
+{
+ mlassert_self();
+
+ *self = _defaults;
+}
+
+void filter_copy_destroy(filter_copy_t* self)
+{
+ mlassert_self();
+}
+
+void filter_copy_set(filter_copy_t* self, int32_t obj_type)
+{
+ mlassert_self();
+
+ switch (obj_type) {
+ case CORE_OBJECT_NONE:
+ self->copy |= 0x1;
+ break;
+ case CORE_OBJECT_PCAP:
+ self->copy |= 0x2;
+ break;
+ case CORE_OBJECT_ETHER:
+ self->copy |= 0x4;
+ break;
+ case CORE_OBJECT_NULL:
+ self->copy |= 0x8;
+ break;
+ case CORE_OBJECT_LOOP:
+ self->copy |= 0x10;
+ break;
+ case CORE_OBJECT_LINUXSLL:
+ self->copy |= 0x20;
+ break;
+ case CORE_OBJECT_IEEE802:
+ self->copy |= 0x40;
+ break;
+ case CORE_OBJECT_GRE:
+ self->copy |= 0x80;
+ break;
+ case CORE_OBJECT_IP:
+ self->copy |= 0x100;
+ break;
+ case CORE_OBJECT_IP6:
+ self->copy |= 0x200;
+ break;
+ case CORE_OBJECT_ICMP:
+ self->copy |= 0x400;
+ break;
+ case CORE_OBJECT_ICMP6:
+ self->copy |= 0x800;
+ break;
+ case CORE_OBJECT_UDP:
+ self->copy |= 0x1000;
+ break;
+ case CORE_OBJECT_TCP:
+ self->copy |= 0x2000;
+ break;
+ case CORE_OBJECT_PAYLOAD:
+ self->copy |= 0x4000;
+ break;
+ case CORE_OBJECT_DNS:
+ self->copy |= 0x8000;
+ break;
+ default:
+ lfatal("unknown type %d", obj_type);
+ }
+}
+
+uint64_t filter_copy_get(filter_copy_t* self, int32_t obj_type)
+{
+ mlassert_self();
+
+ switch (obj_type) {
+ case CORE_OBJECT_NONE:
+ return self->copy & 0x1;
+ case CORE_OBJECT_PCAP:
+ return self->copy & 0x2;
+ case CORE_OBJECT_ETHER:
+ return self->copy & 0x4;
+ case CORE_OBJECT_NULL:
+ return self->copy & 0x8;
+ case CORE_OBJECT_LOOP:
+ return self->copy & 0x10;
+ case CORE_OBJECT_LINUXSLL:
+ return self->copy & 0x20;
+ case CORE_OBJECT_IEEE802:
+ return self->copy & 0x40;
+ case CORE_OBJECT_GRE:
+ return self->copy & 0x80;
+ case CORE_OBJECT_IP:
+ return self->copy & 0x100;
+ case CORE_OBJECT_IP6:
+ return self->copy & 0x200;
+ case CORE_OBJECT_ICMP:
+ return self->copy & 0x400;
+ case CORE_OBJECT_ICMP6:
+ return self->copy & 0x800;
+ case CORE_OBJECT_UDP:
+ return self->copy & 0x1000;
+ case CORE_OBJECT_TCP:
+ return self->copy & 0x2000;
+ case CORE_OBJECT_PAYLOAD:
+ return self->copy & 0x4000;
+ case CORE_OBJECT_DNS:
+ return self->copy & 0x8000;
+ default:
+ lfatal("unknown type %d", obj_type);
+ }
+ return 0;
+}
+
+static void _receive(filter_copy_t* self, const core_object_t* obj)
+{
+ mlassert_self();
+ lassert(obj, "obj is nil");
+
+ core_object_t* outobj = NULL;
+ core_object_t* next = NULL;
+ core_object_t* current = NULL;
+ const core_object_t* srcobj = obj;
+
+ do {
+ if (filter_copy_get(self, srcobj->obj_type)) {
+ next = current;
+ current = core_object_copy(srcobj);
+ if (next == NULL) {
+ next = current;
+ outobj = current;
+ } else {
+ next->obj_prev = current;
+ }
+ }
+ srcobj = srcobj->obj_prev;
+ } while (srcobj != NULL);
+
+ if (outobj == NULL) {
+ lnotice("object discarded (no types to copy)");
+ return;
+ }
+
+ self->recv(self->recv_ctx, outobj);
+}
+
+core_receiver_t filter_copy_receiver(filter_copy_t* self)
+{
+ mlassert_self();
+
+ if (!self->recv) {
+ lfatal("no receiver(s) set");
+ }
+
+ return (core_receiver_t)_receive;
+}
diff --git a/include/dnsjit/filter/copy.h b/include/dnsjit/filter/copy.h
new file mode 100644
index 0000000..d65a552
--- /dev/null
+++ b/include/dnsjit/filter/copy.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019, CZ.NIC, z.s.p.o.
+ * 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 <dnsjit/core/log.h>
+#include <dnsjit/core/object.h>
+#include <dnsjit/core/receiver.h>
+
+#ifndef __dnsjit_filter_copy_h
+#define __dnsjit_filter_copy_h
+
+#include <dnsjit/filter/copy.hh>
+
+#endif
diff --git a/include/dnsjit/filter/copy.hh b/include/dnsjit/filter/copy.hh
new file mode 100644
index 0000000..edf7fc7
--- /dev/null
+++ b/include/dnsjit/filter/copy.hh
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019, CZ.NIC z.s.p.o.
+ * 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")
+
+typedef struct filter_copy {
+ core_log_t _log;
+
+ core_receiver_t recv;
+ void* recv_ctx;
+
+ uint64_t copy;
+} filter_copy_t;
+
+core_log_t* filter_copy_log();
+
+void filter_copy_init(filter_copy_t* self);
+void filter_copy_destroy(filter_copy_t* self);
+void filter_copy_set(filter_copy_t* self, int32_t obj_type);
+uint64_t filter_copy_get(filter_copy_t* self, int32_t obj_type);
+
+core_receiver_t filter_copy_receiver(filter_copy_t* self);
diff --git a/include/dnsjit/filter/copy.lua b/include/dnsjit/filter/copy.lua
new file mode 100644
index 0000000..4be469f
--- /dev/null
+++ b/include/dnsjit/filter/copy.lua
@@ -0,0 +1,74 @@
+-- Copyright (c) 2019 CZ.NIC, z.s.p.o.
+-- 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.filter.copy
+-- Creates a copy of an object chain with selected object types.
+-- local copy = require("dnsjit.filter.copy").new()
+-- local object = require("dnsjit.core.objects")
+-- copy:obj_type(object.PAYLOAD)
+-- copy:obj_type(object.IP6)
+-- channel:receiver(copy)
+--
+-- Filter to create a copy of the object chain with selected object types.
+-- The user is responsible for manually freeing the created object chain.
+module(...,package.seeall)
+
+require("dnsjit.filter.copy_h")
+local object = require("dnsjit.core.object")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "filter_copy_t"
+local filter_copy_t = ffi.typeof(t_name)
+local Copy = {}
+
+-- Create a new Copy filter.
+function Copy.new()
+ local self = {
+ obj = filter_copy_t(),
+ }
+ C.filter_copy_init(self.obj)
+ ffi.gc(self.obj, C.filter_copy_destroy)
+ return setmetatable(self, { __index = Copy })
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Copy:log()
+ if self == nil then
+ return C.filter_copy_log()
+ end
+ return self.obj._log
+end
+
+-- Set the object type to be copied. Can be called multiple times to copy
+-- multiple object types from the object chain.
+function Copy:obj_type(obj_type)
+ C.filter_copy_set(self.obj, obj_type)
+end
+
+-- Return the C functions and context for receiving objects.
+function Copy:receive()
+ return C.filter_copy_receiver(self.obj), self.obj
+end
+
+-- Set the receiver to pass objects to.
+function Copy:receiver(o)
+ self.obj.recv, self.obj.recv_ctx = o:receive()
+end
+
+return Copy
diff --git a/include/dnsjit/filter/ipsplit.c b/include/dnsjit/filter/ipsplit.c
new file mode 100644
index 0000000..8632e2e
--- /dev/null
+++ b/include/dnsjit/filter/ipsplit.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2019-2020, CZ.NIC, z.s.p.o.
+ * 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 "filter/ipsplit.h"
+#include "core/assert.h"
+#include "core/object/ip.h"
+#include "core/object/ip6.h"
+#include "lib/trie.h"
+
+#include <string.h>
+
+typedef struct _filter_ipsplit {
+ filter_ipsplit_t pub;
+
+ trie_t* trie;
+ uint32_t weight_total;
+} _filter_ipsplit_t;
+
+typedef struct _client {
+ /* Receiver-specific client ID (1..N) in host byte order. */
+ /* Client ID starts at 1 to avoid issues with lua. */
+ uint8_t id[4];
+
+ filter_ipsplit_recv_t* recv;
+} _client_t;
+
+#define _self ((_filter_ipsplit_t*)self)
+
+static core_log_t _log = LOG_T_INIT("filter.ipsplit");
+static filter_ipsplit_t _defaults = {
+ LOG_T_INIT_OBJ("filter.ipsplit"),
+ IPSPLIT_MODE_SEQUENTIAL, IPSPLIT_OVERWRITE_NONE,
+ 0,
+ NULL
+};
+
+core_log_t* filter_ipsplit_log()
+{
+ return &_log;
+}
+
+filter_ipsplit_t* filter_ipsplit_new()
+{
+ filter_ipsplit_t* self;
+
+ mlfatal_oom(self = malloc(sizeof(_filter_ipsplit_t)));
+ *self = _defaults;
+ lfatal_oom(_self->trie = trie_create(NULL));
+ _self->weight_total = 0;
+
+ return self;
+}
+
+static int _free_trie_value(trie_val_t* val, void* ctx)
+{
+ free(*val);
+ return 0;
+}
+
+void filter_ipsplit_free(filter_ipsplit_t* self)
+{
+ filter_ipsplit_recv_t* first;
+ filter_ipsplit_recv_t* r;
+ mlassert_self();
+
+ trie_apply(_self->trie, _free_trie_value, NULL);
+ trie_free(_self->trie);
+
+ if (self->recv) {
+ first = self->recv;
+ do {
+ r = self->recv->next;
+ free(self->recv);
+ self->recv = r;
+ } while (self->recv != first);
+ }
+
+ free(self);
+}
+
+void filter_ipsplit_add(filter_ipsplit_t* self, core_receiver_t recv, void* ctx, uint32_t weight)
+{
+ filter_ipsplit_recv_t* r;
+ mlassert_self();
+ lassert(recv, "recv is nil");
+ lassert(weight > 0, "weight must be positive integer");
+
+ _self->weight_total += weight;
+
+ lfatal_oom(r = malloc(sizeof(filter_ipsplit_recv_t)));
+ r->recv = recv;
+ r->ctx = ctx;
+ r->n_clients = 0;
+ r->weight = weight;
+
+ if (!self->recv) {
+ r->next = r;
+ self->recv = r;
+ } else {
+ r->next = self->recv->next;
+ self->recv->next = r;
+ }
+}
+
+/*
+ * Use portable pseudo-random number generator.
+ */
+static uint32_t _rand_val = 1;
+
+static uint32_t _rand()
+{
+ _rand_val = ((_rand_val * 1103515245) + 12345) & 0x7fffffff;
+ return _rand_val;
+}
+
+void filter_ipsplit_srand(uint32_t seed)
+{
+ _rand_val = seed;
+}
+
+static void _assign_client_to_receiver(filter_ipsplit_t* self, _client_t* client)
+{
+ uint32_t id = 0;
+ filter_ipsplit_recv_t* recv = 0;
+
+ switch (self->mode) {
+ case IPSPLIT_MODE_SEQUENTIAL:
+ recv = self->recv;
+ id = ++recv->n_clients;
+ /* When *weight* clients are assigned, switch to next receiver. */
+ if (recv->n_clients % recv->weight == 0)
+ self->recv = recv->next;
+ break;
+ case IPSPLIT_MODE_RANDOM: {
+ /* Get random number from [1, weight_total], then iterate through
+ * receivers until their weights add up to at least this value. */
+ int32_t random = (int32_t)(_rand() % _self->weight_total) + 1;
+ while (random > 0) {
+ random -= self->recv->weight;
+ if (random > 0)
+ self->recv = self->recv->next;
+ }
+ recv = self->recv;
+ id = ++recv->n_clients;
+ break;
+ }
+ default:
+ lfatal("invalid ipsplit mode");
+ }
+
+ client->recv = recv;
+ memcpy(client->id, &id, sizeof(client->id));
+}
+
+/*
+ * Optionally, write client ID into byte 0-3 of src/dst IP address in the packet.
+ *
+ * Client ID is a 4-byte array in host byte order.
+ */
+static void _overwrite(filter_ipsplit_t* self, core_object_t* obj, _client_t* client)
+{
+ mlassert_self();
+ lassert(obj, "invalid object");
+ lassert(client, "invalid client");
+
+ core_object_ip_t* ip;
+ core_object_ip6_t* ip6;
+
+ switch (self->overwrite) {
+ case IPSPLIT_OVERWRITE_NONE:
+ return;
+ case IPSPLIT_OVERWRITE_SRC:
+ if (obj->obj_type == CORE_OBJECT_IP) {
+ ip = (core_object_ip_t*)obj;
+ memcpy(&ip->src, client->id, sizeof(client->id));
+ } else if (obj->obj_type == CORE_OBJECT_IP6) {
+ ip6 = (core_object_ip6_t*)obj;
+ memcpy(&ip6->src, client->id, sizeof(client->id));
+ }
+ break;
+ case IPSPLIT_OVERWRITE_DST:
+ if (obj->obj_type == CORE_OBJECT_IP) {
+ ip = (core_object_ip_t*)obj;
+ memcpy(&ip->dst, client->id, sizeof(client->id));
+ } else if (obj->obj_type == CORE_OBJECT_IP6) {
+ ip6 = (core_object_ip6_t*)obj;
+ memcpy(&ip6->dst, client->id, sizeof(client->id));
+ }
+ break;
+ default:
+ lfatal("invalid overwrite mode");
+ }
+}
+
+static void _receive(filter_ipsplit_t* self, const core_object_t* obj)
+{
+ mlassert_self();
+
+ /* Find ip/ip6 object in chain. */
+ core_object_t* pkt = (core_object_t*)obj;
+ while (pkt != NULL) {
+ if (pkt->obj_type == CORE_OBJECT_IP || pkt->obj_type == CORE_OBJECT_IP6)
+ break;
+ pkt = (core_object_t*)pkt->obj_prev;
+ }
+ if (pkt == NULL) {
+ self->discarded++;
+ lwarning("packet discarded (missing ip/ip6 object)");
+ return;
+ }
+
+ /* Lookup IPv4/IPv6 address in trie (prefix-tree). Inserts new node if not found. */
+ trie_val_t* node = 0;
+ switch (pkt->obj_type) {
+ case CORE_OBJECT_IP: {
+ core_object_ip_t* ip = (core_object_ip_t*)pkt;
+ node = trie_get_ins(_self->trie, ip->src, sizeof(ip->src));
+ break;
+ }
+ case CORE_OBJECT_IP6: {
+ core_object_ip6_t* ip6 = (core_object_ip6_t*)pkt;
+ node = trie_get_ins(_self->trie, ip6->src, sizeof(ip6->src));
+ break;
+ }
+ default:
+ lfatal("unsupported object type");
+ }
+ lassert(node, "trie failure");
+
+ _client_t* client;
+ if (*node == NULL) { /* IP address not found in tree -> create new client. */
+ lfatal_oom(client = malloc(sizeof(_client_t)));
+ *node = (void*)client;
+ _assign_client_to_receiver(self, client);
+ }
+
+ client = (_client_t*)*node;
+ _overwrite(self, pkt, client);
+ client->recv->recv(client->recv->ctx, obj);
+}
+
+core_receiver_t filter_ipsplit_receiver(filter_ipsplit_t* self)
+{
+ mlassert_self();
+
+ if (!self->recv) {
+ lfatal("no receiver(s) set");
+ }
+
+ return (core_receiver_t)_receive;
+}
diff --git a/include/dnsjit/filter/ipsplit.h b/include/dnsjit/filter/ipsplit.h
new file mode 100644
index 0000000..aa64878
--- /dev/null
+++ b/include/dnsjit/filter/ipsplit.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019-2020 CZ.NIC, z.s.p.o.
+ * 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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+
+#ifndef __dnsjit_filter_ipsplit_h
+#define __dnsjit_filter_ipsplit_h
+
+#include <dnsjit/filter/ipsplit.hh>
+
+#endif
diff --git a/include/dnsjit/filter/ipsplit.hh b/include/dnsjit/filter/ipsplit.hh
new file mode 100644
index 0000000..c362ac3
--- /dev/null
+++ b/include/dnsjit/filter/ipsplit.hh
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019-2020, CZ.NIC, z.s.p.o.
+ * 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")
+
+typedef struct filter_ipsplit_recv filter_ipsplit_recv_t;
+struct filter_ipsplit_recv {
+ filter_ipsplit_recv_t* next;
+
+ core_receiver_t recv;
+ void* ctx;
+
+ uint32_t n_clients; /* Total number of clients assigned to this receiver. */
+
+ uint32_t weight;
+};
+
+typedef struct filter_ipsplit {
+ core_log_t _log;
+
+ enum {
+ IPSPLIT_MODE_SEQUENTIAL = 0,
+ IPSPLIT_MODE_RANDOM = 1
+ } mode;
+ enum {
+ IPSPLIT_OVERWRITE_NONE = 0,
+ IPSPLIT_OVERWRITE_SRC = 1,
+ IPSPLIT_OVERWRITE_DST = 2
+ } overwrite;
+
+ uint64_t discarded;
+
+ filter_ipsplit_recv_t* recv;
+} filter_ipsplit_t;
+
+core_log_t* filter_ipsplit_log();
+
+filter_ipsplit_t* filter_ipsplit_new();
+void filter_ipsplit_free(filter_ipsplit_t* self);
+void filter_ipsplit_add(filter_ipsplit_t* self, core_receiver_t recv, void* ctx, uint32_t weight);
+void filter_ipsplit_srand(unsigned int seed);
+
+core_receiver_t filter_ipsplit_receiver(filter_ipsplit_t* self);
diff --git a/include/dnsjit/filter/ipsplit.lua b/include/dnsjit/filter/ipsplit.lua
new file mode 100644
index 0000000..cca0249
--- /dev/null
+++ b/include/dnsjit/filter/ipsplit.lua
@@ -0,0 +1,122 @@
+-- Copyright (c) 2019-2020 CZ.NIC, z.s.p.o.
+-- 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.filter.ipsplit
+-- Pass objects to receivers based on the source IP address
+-- local ipsplit = require("dnsjit.filter.ipsplit").new()
+-- ipsplit.receiver(...)
+-- ipsplit.receiver(...)
+-- ipsplit.receiver(...)
+-- input.receiver(ipsplit)
+--
+-- The filter passes objects to other receivers.
+-- Object chains without IPv4/IPv6 packet are discarded.
+-- Packets which have the same source IP address are considered to be sent
+-- from the same "client".
+-- When the first packet from a client is processed, the client is assigned
+-- to a receiver.
+-- All objects from this client will be passed to the assigned receiver.
+-- The filter can also write a receiver-specific client ID (starting from 1)
+-- to the source or destination IP in the packet.
+module(...,package.seeall)
+
+require("dnsjit.filter.ipsplit_h")
+local bit = require("bit")
+local object = require("dnsjit.core.objects")
+local ffi = require("ffi")
+local C = ffi.C
+
+local IpSplit = {}
+
+-- Create a new IpSplit filter.
+function IpSplit.new()
+ local self = {
+ obj = C.filter_ipsplit_new(),
+ }
+ ffi.gc(self.obj, C.filter_ipsplit_free)
+ return setmetatable(self, { __index = IpSplit })
+end
+
+-- Return the Log object to control logging of this instance or module.
+function IpSplit:log()
+ if self == nil then
+ return C.filter_ipsplit_log()
+ end
+ return self.obj._log
+end
+
+-- Return the C functions and context for receiving objects.
+function IpSplit:receive()
+ local recv = C.filter_ipsplit_receiver(self.obj)
+ return recv, self.obj
+end
+
+-- Set the receiver to pass objects to, this can be called multiple times to
+-- set additional receivers.
+-- The weight parameter can be used to adjust distribution of clients among
+-- receivers.
+-- Weight must be a positive integer (default is 1).
+function IpSplit:receiver(o, weight)
+ local recv, ctx = o:receive()
+ if weight == nil then
+ weight = 1
+ end
+ C.filter_ipsplit_add(self.obj, recv, ctx, weight)
+end
+
+-- Number of input packets discarded due to various reasons.
+-- To investigate causes, run with increased logging level.
+function IpSplit:discarded()
+ return tonumber(self.obj.discarded)
+end
+
+-- Set the client assignment mode to sequential.
+-- Assigns `weight` clients to a receiver before continuing with the next
+-- receiver (default mode).
+function IpSplit:sequential()
+ self.obj.mode = "IPSPLIT_MODE_SEQUENTIAL"
+end
+
+-- Set the client assignment mode to random.
+-- Each client is randomly assigned to a receiver (weight affects the
+-- probability).
+-- The client assignment is stable (and portable) for given seed.
+function IpSplit:random(seed)
+ self.obj.mode = "IPSPLIT_MODE_RANDOM"
+ if seed then
+ C.filter_ipsplit_srand(seed)
+ end
+end
+
+-- Don't overwrite source or destination IP (default).
+function IpSplit:overwrite_none()
+ self.obj.overwrite = "IPSPLIT_OVERWRITE_NONE"
+end
+
+-- Write receiver-specific client ID to bytes 0-3 of source IP (host byte order).
+function IpSplit:overwrite_src()
+ self.obj.overwrite = "IPSPLIT_OVERWRITE_SRC"
+end
+
+-- Write receiver-specific client ID to bytes 0-3 of destination IP (host byte
+-- order).
+function IpSplit:overwrite_dst()
+ self.obj.overwrite = "IPSPLIT_OVERWRITE_DST"
+end
+
+return IpSplit
diff --git a/include/dnsjit/filter/layer.c b/include/dnsjit/filter/layer.c
new file mode 100644
index 0000000..b360ca3
--- /dev/null
+++ b/include/dnsjit/filter/layer.c
@@ -0,0 +1,689 @@
+/*
+ * 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 "filter/layer.h"
+#include "core/assert.h"
+
+#include <string.h>
+#include <pcap/pcap.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#ifdef HAVE_NET_ETHERNET_H
+#include <net/ethernet.h>
+#endif
+#ifdef HAVE_NET_ETHERTYPES_H
+#include <net/ethertypes.h>
+#endif
+#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
+
+#define N_IEEE802 3
+
+static core_log_t _log = LOG_T_INIT("filter.layer");
+static filter_layer_t _defaults = {
+ LOG_T_INIT_OBJ("filter.layer"),
+ 0, 0,
+ 0, 0,
+ 0,
+ CORE_OBJECT_NULL_INIT(0),
+ CORE_OBJECT_ETHER_INIT(0),
+ CORE_OBJECT_LOOP_INIT(0),
+ CORE_OBJECT_LINUXSLL_INIT(0),
+ 0, { CORE_OBJECT_IEEE802_INIT(0), CORE_OBJECT_IEEE802_INIT(0), CORE_OBJECT_IEEE802_INIT(0) },
+ CORE_OBJECT_IP_INIT(0),
+ CORE_OBJECT_IP6_INIT(0),
+ CORE_OBJECT_GRE_INIT(0),
+ CORE_OBJECT_ICMP_INIT(0),
+ CORE_OBJECT_ICMP6_INIT(0),
+ CORE_OBJECT_UDP_INIT(0),
+ CORE_OBJECT_TCP_INIT(0),
+ CORE_OBJECT_PAYLOAD_INIT(0)
+};
+
+core_log_t* filter_layer_log()
+{
+ return &_log;
+}
+
+void filter_layer_init(filter_layer_t* self)
+{
+ mlassert_self();
+
+ *self = _defaults;
+}
+
+void filter_layer_destroy(filter_layer_t* self)
+{
+ mlassert_self();
+}
+
+#define need4x2(v1, v2, p, l) \
+ if (l < 1) { \
+ break; \
+ } \
+ v1 = (*p) >> 4; \
+ v2 = (*p) & 0xf; \
+ p += 1; \
+ l -= 1
+
+#define need8(v, p, l) \
+ if (l < 1) { \
+ break; \
+ } \
+ v = *p; \
+ p += 1; \
+ l -= 1
+
+static inline uint16_t _need16(const void* ptr)
+{
+ uint16_t v;
+ memcpy(&v, ptr, sizeof(v));
+ return be16toh(v);
+}
+
+#define need16(v, p, l) \
+ if (l < 2) { \
+ break; \
+ } \
+ v = _need16(p); \
+ p += 2; \
+ l -= 2
+
+#define needr16(v, p, l) \
+ if (l < 2) { \
+ break; \
+ } \
+ v = bswap_16(_need16(p)); \
+ p += 2; \
+ l -= 2
+
+static inline uint32_t _need32(const void* ptr)
+{
+ uint32_t v;
+ memcpy(&v, ptr, sizeof(v));
+ return be32toh(v);
+}
+
+#define need32(v, p, l) \
+ if (l < 4) { \
+ break; \
+ } \
+ v = _need32(p); \
+ p += 4; \
+ l -= 4
+
+#define needr32(v, p, l) \
+ if (l < 4) { \
+ break; \
+ } \
+ v = bswap_32(_need32(p)); \
+ p += 4; \
+ l -= 4
+
+#define needxb(b, x, p, l) \
+ if (l < x) { \
+ break; \
+ } \
+ memcpy(b, p, x); \
+ p += x; \
+ l -= x
+
+#define advancexb(x, p, l) \
+ if (l < x) { \
+ break; \
+ } \
+ p += x; \
+ l -= x
+
+//static int _ip(filter_layer_t* self, const core_object_t* obj, const unsigned char* pkt, size_t len);
+
+static inline int _proto(filter_layer_t* self, uint8_t proto, const core_object_t* obj, const unsigned char* pkt, size_t len)
+{
+ switch (proto) {
+ case IPPROTO_GRE: {
+ core_object_gre_t* gre = &self->gre;
+ gre->obj_prev = obj;
+
+ need16(gre->gre_flags, pkt, len);
+ need16(gre->ether_type, pkt, len);
+
+ /* TODO: Incomplete, check RFC 1701 */
+
+ self->produced = (core_object_t*)gre;
+
+ // if (gre.gre_flags & 0x1) {
+ // need16(gre.checksum, pkt, len);
+ // }
+ // if (gre.gre_flags & 0x4) {
+ // need16(gre.key, pkt, len);
+ // }
+ // if (gre.gre_flags & 0x8) {
+ // need16(gre.sequence, pkt, len);
+ // }
+ //
+ // switch (gre.ether_type) {
+ // case ETHERTYPE_IP:
+ // case ETHERTYPE_IPV6:
+ // return _ip(self, (core_object_t*)gre, pkt, len);
+ //
+ // default:
+ // break;
+ // }
+ break;
+ }
+ case IPPROTO_ICMP: {
+ core_object_icmp_t* icmp = &self->icmp;
+ icmp->obj_prev = obj;
+
+ need8(icmp->type, pkt, len);
+ need8(icmp->code, pkt, len);
+ need16(icmp->cksum, pkt, len);
+
+ self->produced = (core_object_t*)icmp;
+ break;
+ }
+ case IPPROTO_ICMPV6: {
+ core_object_icmp6_t* icmp6 = &self->icmp6;
+ icmp6->obj_prev = obj;
+
+ need8(icmp6->type, pkt, len);
+ need8(icmp6->code, pkt, len);
+ need16(icmp6->cksum, pkt, len);
+
+ self->produced = (core_object_t*)icmp6;
+ break;
+ }
+ case IPPROTO_UDP: {
+ core_object_udp_t* udp = &self->udp;
+ core_object_payload_t* payload = &self->payload;
+ udp->obj_prev = obj;
+
+ need16(udp->sport, pkt, len);
+ need16(udp->dport, pkt, len);
+ need16(udp->ulen, pkt, len);
+ need16(udp->sum, pkt, len);
+
+ payload->obj_prev = (core_object_t*)udp;
+
+ /* Check for padding */
+ if (len > udp->ulen) {
+ payload->padding = len - udp->ulen;
+ payload->len = len - payload->padding;
+ } else {
+ payload->padding = 0;
+ payload->len = len;
+ }
+ payload->payload = (uint8_t*)pkt;
+
+ self->produced = (core_object_t*)payload;
+ break;
+ }
+ case IPPROTO_TCP: {
+ core_object_tcp_t* tcp = &self->tcp;
+ core_object_payload_t* payload = &self->payload;
+ tcp->obj_prev = obj;
+
+ need16(tcp->sport, pkt, len);
+ need16(tcp->dport, pkt, len);
+ need32(tcp->seq, pkt, len);
+ need32(tcp->ack, pkt, len);
+ need4x2(tcp->off, tcp->x2, pkt, len);
+ need8(tcp->flags, pkt, len);
+ need16(tcp->win, pkt, len);
+ need16(tcp->sum, pkt, len);
+ need16(tcp->urp, pkt, len);
+ if (tcp->off > 5) {
+ tcp->opts_len = (tcp->off - 5) * 4;
+ needxb(tcp->opts, tcp->opts_len, pkt, len);
+ } else {
+ tcp->opts_len = 0;
+ }
+
+ payload->obj_prev = (core_object_t*)tcp;
+
+ /* Check for padding */
+ if (obj->obj_type == CORE_OBJECT_IP && len > (((const core_object_ip_t*)obj)->len - (((const core_object_ip_t*)obj)->hl * 4))) {
+ payload->padding = len - (((const core_object_ip_t*)obj)->len - (((const core_object_ip_t*)obj)->hl * 4));
+ payload->len = len - payload->padding;
+ } else if (obj->obj_type == CORE_OBJECT_IP6 && len > ((const core_object_ip6_t*)obj)->plen) {
+ payload->padding = len - ((const core_object_ip6_t*)obj)->plen;
+ payload->len = len - payload->padding;
+ } else {
+ payload->padding = 0;
+ payload->len = len;
+ }
+
+ payload->payload = (uint8_t*)pkt;
+
+ self->produced = (core_object_t*)payload;
+ break;
+ }
+ default:
+ self->produced = obj;
+ break;
+ }
+
+ return 0;
+}
+
+static inline int _ip(filter_layer_t* self, const core_object_t* obj, const unsigned char* pkt, size_t len)
+{
+ if (len) {
+ switch ((*pkt >> 4)) {
+ case 4: {
+ core_object_ip_t* ip = &self->ip;
+
+ ip->obj_prev = obj;
+
+ need4x2(ip->v, ip->hl, pkt, len);
+ need8(ip->tos, pkt, len);
+ need16(ip->len, pkt, len);
+ need16(ip->id, pkt, len);
+ need16(ip->off, pkt, len);
+ need8(ip->ttl, pkt, len);
+ need8(ip->p, pkt, len);
+ need16(ip->sum, pkt, len);
+ needxb(&ip->src, 4, pkt, len);
+ needxb(&ip->dst, 4, pkt, len);
+
+ /* TODO: IPv4 options */
+
+ if (ip->hl < 5)
+ break;
+ if (ip->hl > 5) {
+ advancexb((ip->hl - 5) * 4, pkt, len);
+ }
+
+ /* Check reported length for missing payload */
+ if (ip->len < (ip->hl * 4)) {
+ break;
+ }
+ if (len < (ip->len - (ip->hl * 4))) {
+ break;
+ }
+
+ if (ip->off & 0x2000 || ip->off & 0x1fff) {
+ core_object_payload_t* payload = &self->payload;
+
+ payload->obj_prev = (core_object_t*)ip;
+
+ /* Check for padding */
+ if (len > (ip->len - (ip->hl * 4))) {
+ payload->padding = len - (ip->len - (ip->hl * 4));
+ payload->len = len - payload->padding;
+ } else {
+ payload->padding = 0;
+ payload->len = len;
+ }
+ payload->payload = (uint8_t*)pkt;
+
+ self->produced = (core_object_t*)payload;
+ return 0;
+ }
+
+ return _proto(self, ip->p, (core_object_t*)ip, pkt, len);
+ }
+ case 6: {
+ core_object_ip6_t* ip6 = &self->ip6;
+ struct ip6_ext ext;
+
+ ip6->obj_prev = obj;
+ ip6->is_frag = ip6->have_rtdst = 0;
+
+ need32(ip6->flow, pkt, len);
+ need16(ip6->plen, pkt, len);
+ need8(ip6->nxt, pkt, len);
+ need8(ip6->hlim, pkt, len);
+ needxb(&ip6->src, 16, pkt, len);
+ needxb(&ip6->dst, 16, pkt, len);
+
+ /* Check reported length for missing payload */
+ if (len < ip6->plen) {
+ break;
+ }
+
+ ext.ip6e_nxt = ip6->nxt;
+ ext.ip6e_len = 0;
+ while (ext.ip6e_nxt != IPPROTO_NONE
+ && ext.ip6e_nxt != IPPROTO_GRE
+ && ext.ip6e_nxt != IPPROTO_ICMPV6
+ && ext.ip6e_nxt != IPPROTO_UDP
+ && ext.ip6e_nxt != IPPROTO_TCP) {
+
+ /*
+ * Advance to the start of next header, this may not be needed
+ * if it's the first header or if the header is supported.
+ */
+ if (ext.ip6e_len) {
+ advancexb(ext.ip6e_len * 8, pkt, len);
+ }
+
+ /* TODO: Store IPv6 headers? */
+
+ /* Handle supported headers */
+ if (ext.ip6e_nxt == IPPROTO_FRAGMENT) {
+ if (ip6->is_frag) {
+ return 1;
+ }
+ need8(ext.ip6e_nxt, pkt, len);
+ need8(ext.ip6e_len, pkt, len);
+ if (ext.ip6e_len) {
+ return 1;
+ }
+ need16(ip6->frag_offlg, pkt, len);
+ need32(ip6->frag_ident, pkt, len);
+ ip6->is_frag = 1;
+ } else if (ext.ip6e_nxt == IPPROTO_ROUTING) {
+ struct ip6_rthdr rthdr;
+
+ if (ip6->have_rtdst) {
+ return 1;
+ }
+
+ need8(ext.ip6e_nxt, pkt, len);
+ need8(ext.ip6e_len, pkt, len);
+ need8(rthdr.ip6r_type, pkt, len);
+ need8(rthdr.ip6r_segleft, pkt, len);
+ advancexb(4, pkt, len);
+
+ if (!rthdr.ip6r_type && rthdr.ip6r_segleft) {
+ if (ext.ip6e_len & 1) {
+ return 1;
+ }
+ if (ext.ip6e_len > 2) {
+ advancexb(ext.ip6e_len - 2, pkt, len);
+ }
+ needxb(ip6->rtdst, 16, pkt, len);
+ ip6->have_rtdst = 1;
+ }
+ } else {
+ need8(ext.ip6e_nxt, pkt, len);
+ need8(ext.ip6e_len, pkt, len);
+ advancexb(6, pkt, len);
+ }
+ }
+
+ if (ext.ip6e_nxt == IPPROTO_NONE || ip6->is_frag) {
+ core_object_payload_t* payload = &self->payload;
+
+ payload->obj_prev = (core_object_t*)ip6;
+
+ /* Check for padding */
+ if (len > ip6->plen) {
+ payload->padding = len - ip6->plen;
+ payload->len = len - payload->padding;
+ } else {
+ payload->padding = 0;
+ payload->len = len;
+ }
+ payload->payload = (uint8_t*)pkt;
+
+ self->produced = (core_object_t*)payload;
+ return 0;
+ }
+
+ return _proto(self, ext.ip6e_nxt, (core_object_t*)ip6, pkt, len);
+ }
+ default:
+ break;
+ }
+ }
+
+ self->produced = obj;
+
+ return 0;
+}
+
+static inline int _ieee802(filter_layer_t* self, uint16_t tpid, const core_object_t* obj, const unsigned char* pkt, size_t len)
+{
+ core_object_ieee802_t* ieee802 = &self->ieee802[self->n_ieee802];
+ uint16_t tci;
+
+ ieee802->obj_prev = obj;
+
+ for (;;) {
+ ieee802->tpid = tpid;
+ need16(tci, pkt, len);
+ ieee802->pcp = (tci & 0xe000) >> 13;
+ ieee802->dei = (tci & 0x1000) >> 12;
+ ieee802->vid = tci & 0x0fff;
+ need16(ieee802->ether_type, pkt, len);
+
+ switch (ieee802->ether_type) {
+ case 0x88a8: /* 802.1ad */
+ case 0x9100: /* 802.1 QinQ non-standard */
+ self->n_ieee802++;
+ if (self->n_ieee802 < N_IEEE802) {
+ obj = (const core_object_t*)ieee802;
+ ieee802 = &self->ieee802[self->n_ieee802];
+ ieee802->obj_prev = obj;
+ tpid = ieee802->ether_type;
+ continue;
+ }
+ return 1;
+
+ case ETHERTYPE_IP:
+ case ETHERTYPE_IPV6:
+ return _ip(self, (core_object_t*)ieee802, pkt, len);
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ self->produced = obj;
+
+ return 0;
+}
+
+static inline int _link(filter_layer_t* self, const core_object_pcap_t* pcap)
+{
+ const unsigned char* pkt;
+ size_t len;
+
+ self->n_ieee802 = 0;
+
+ pkt = pcap->bytes;
+ len = pcap->caplen;
+
+ switch (pcap->linktype) {
+ case DLT_NULL: {
+ core_object_null_t* null = &self->null;
+ null->obj_prev = (core_object_t*)pcap;
+
+ if (pcap->is_swapped) {
+ needr32(null->family, pkt, len);
+ } else {
+ need32(null->family, pkt, len);
+ }
+
+ switch (null->family) {
+ case 2:
+ case 24:
+ case 28:
+ case 30:
+ return _ip(self, (core_object_t*)null, pkt, len);
+
+ default:
+ break;
+ }
+ break;
+ }
+ case DLT_EN10MB: {
+ core_object_ether_t* ether = &self->ether;
+ ether->obj_prev = (core_object_t*)pcap;
+
+ needxb(ether->dhost, 6, pkt, len);
+ needxb(ether->shost, 6, pkt, len);
+ need16(ether->type, pkt, len);
+
+ switch (ether->type) {
+ case 0x8100: /* 802.1q */
+ case 0x88a8: /* 802.1ad */
+ case 0x9100: /* 802.1 QinQ non-standard */
+ return _ieee802(self, ether->type, (core_object_t*)ether, pkt, len);
+
+ case ETHERTYPE_IP:
+ case ETHERTYPE_IPV6:
+ return _ip(self, (core_object_t*)ether, pkt, len);
+
+ default:
+ break;
+ }
+ break;
+ }
+ case DLT_LOOP: {
+ core_object_loop_t* loop = &self->loop;
+ loop->obj_prev = (core_object_t*)pcap;
+
+ need32(loop->family, pkt, len);
+
+ switch (loop->family) {
+ case 2:
+ case 24:
+ case 28:
+ case 30:
+ return _ip(self, (core_object_t*)loop, pkt, len);
+
+ default:
+ break;
+ }
+ break;
+ }
+ case DLT_RAW:
+#ifdef DLT_IPV4
+ case DLT_IPV4:
+#endif
+#ifdef DLT_IPV6
+ case DLT_IPV6:
+#endif
+ return _ip(self, (core_object_t*)pcap, pkt, len);
+ case DLT_LINUX_SLL: {
+ core_object_linuxsll_t* linuxsll = &self->linuxsll;
+ linuxsll->obj_prev = (core_object_t*)pcap;
+
+ need16(linuxsll->packet_type, pkt, len);
+ need16(linuxsll->arp_hardware, pkt, len);
+ need16(linuxsll->link_layer_address_length, pkt, len);
+ needxb(linuxsll->link_layer_address, 8, pkt, len);
+ need16(linuxsll->ether_type, pkt, len);
+
+ switch (linuxsll->ether_type) {
+ case 0x8100: /* 802.1q */
+ case 0x88a8: /* 802.1ad */
+ case 0x9100: /* 802.1 QinQ non-standard */
+ return _ieee802(self, linuxsll->ether_type, (core_object_t*)linuxsll, pkt, len);
+
+ case ETHERTYPE_IP:
+ case ETHERTYPE_IPV6:
+ return _ip(self, (core_object_t*)linuxsll, pkt, len);
+
+ default:
+ break;
+ }
+ break;
+ }
+ /* TODO: These might be interesting to implement
+ case DLT_IPNET:
+ case DLT_PKTAP:
+ */
+ default:
+ break;
+ }
+
+ self->produced = (core_object_t*)pcap;
+
+ return 0;
+}
+
+static void _receive(filter_layer_t* self, const core_object_t* obj)
+{
+ mlassert_self();
+ lassert(obj, "obj is nil");
+
+ if (!self->recv) {
+ lfatal("no receiver set");
+ }
+ if (obj->obj_type != CORE_OBJECT_PCAP) {
+ lfatal("obj is not CORE_OBJECT_PCAP");
+ }
+
+ if (!_link(self, (core_object_pcap_t*)obj)) {
+ self->recv(self->ctx, self->produced);
+ }
+}
+
+core_receiver_t filter_layer_receiver()
+{
+ return (core_receiver_t)_receive;
+}
+
+static const core_object_t* _produce(filter_layer_t* self)
+{
+ const core_object_t* obj;
+ mlassert_self();
+
+ obj = self->prod(self->prod_ctx);
+ if (!obj || obj->obj_type != CORE_OBJECT_PCAP || _link(self, (core_object_pcap_t*)obj)) {
+ return 0;
+ }
+
+ return self->produced;
+}
+
+core_producer_t filter_layer_producer(filter_layer_t* self)
+{
+ mlassert_self();
+
+ if (!self->prod) {
+ lfatal("no producer set");
+ }
+
+ return (core_producer_t)_produce;
+}
diff --git a/include/dnsjit/filter/layer.h b/include/dnsjit/filter/layer.h
new file mode 100644
index 0000000..d7dde4a
--- /dev/null
+++ b/include/dnsjit/filter/layer.h
@@ -0,0 +1,44 @@
+/*
+ * 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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+#include <dnsjit/core/producer.h>
+#include <dnsjit/core/object/pcap.h>
+#include <dnsjit/core/object/null.h>
+#include <dnsjit/core/object/ether.h>
+#include <dnsjit/core/object/loop.h>
+#include <dnsjit/core/object/linuxsll.h>
+#include <dnsjit/core/object/ieee802.h>
+#include <dnsjit/core/object/ip.h>
+#include <dnsjit/core/object/ip6.h>
+#include <dnsjit/core/object/gre.h>
+#include <dnsjit/core/object/icmp.h>
+#include <dnsjit/core/object/icmp6.h>
+#include <dnsjit/core/object/udp.h>
+#include <dnsjit/core/object/tcp.h>
+#include <dnsjit/core/object/payload.h>
+
+#ifndef __dnsjit_filter_layer_h
+#define __dnsjit_filter_layer_h
+
+#include <dnsjit/filter/layer.hh>
+
+#endif
diff --git a/include/dnsjit/filter/layer.hh b/include/dnsjit/filter/layer.hh
new file mode 100644
index 0000000..f8f260b
--- /dev/null
+++ b/include/dnsjit/filter/layer.hh
@@ -0,0 +1,70 @@
+/*
+ * 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")
+//lua:require("dnsjit.core.object.null_h")
+//lua:require("dnsjit.core.object.ether_h")
+//lua:require("dnsjit.core.object.loop_h")
+//lua:require("dnsjit.core.object.linuxsll_h")
+//lua:require("dnsjit.core.object.ieee802_h")
+//lua:require("dnsjit.core.object.ip_h")
+//lua:require("dnsjit.core.object.ip6_h")
+//lua:require("dnsjit.core.object.gre_h")
+//lua:require("dnsjit.core.object.icmp_h")
+//lua:require("dnsjit.core.object.icmp6_h")
+//lua:require("dnsjit.core.object.udp_h")
+//lua:require("dnsjit.core.object.tcp_h")
+//lua:require("dnsjit.core.object.payload_h")
+
+typedef struct filter_layer {
+ core_log_t _log;
+ core_receiver_t recv;
+ void* ctx;
+
+ core_producer_t prod;
+ void* prod_ctx;
+
+ const core_object_t* produced;
+ core_object_null_t null;
+ core_object_ether_t ether;
+ core_object_loop_t loop;
+ core_object_linuxsll_t linuxsll;
+ size_t n_ieee802;
+ core_object_ieee802_t ieee802[3]; // N_IEEE802
+ core_object_ip_t ip;
+ core_object_ip6_t ip6;
+ core_object_gre_t gre;
+ core_object_icmp_t icmp;
+ core_object_icmp6_t icmp6;
+ core_object_udp_t udp;
+ core_object_tcp_t tcp;
+ core_object_payload_t payload;
+} filter_layer_t;
+
+core_log_t* filter_layer_log();
+
+void filter_layer_init(filter_layer_t* self);
+void filter_layer_destroy(filter_layer_t* self);
+
+core_receiver_t filter_layer_receiver();
+core_producer_t filter_layer_producer(filter_layer_t* self);
diff --git a/include/dnsjit/filter/layer.lua b/include/dnsjit/filter/layer.lua
new file mode 100644
index 0000000..74c56ba
--- /dev/null
+++ b/include/dnsjit/filter/layer.lua
@@ -0,0 +1,93 @@
+-- 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.filter.layer
+-- Parse the ether/IP stack
+-- local filter = require("dnsjit.filter.layer").new()
+--
+-- Parse the ether/IP stack of the received objects and send the top most
+-- object to the receivers.
+-- Objects are chained which each layer in the stack with the top most first.
+-- Currently supports input
+-- .IR dnsjit.core.object.pcap .
+module(...,package.seeall)
+
+require("dnsjit.filter.layer_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "filter_layer_t"
+local filter_layer_t = ffi.typeof(t_name)
+local Layer = {}
+
+-- Create a new Layer filter.
+function Layer.new()
+ local self = {
+ _receiver = nil,
+ obj = filter_layer_t(),
+ }
+ C.filter_layer_init(self.obj)
+ ffi.gc(self.obj, C.filter_layer_destroy)
+ return setmetatable(self, { __index = Layer })
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Layer:log()
+ if self == nil then
+ return C.filter_layer_log()
+ end
+ return self.obj._log
+end
+
+-- Return the C functions and context for receiving objects.
+function Layer:receive()
+ return C.filter_layer_receiver(), self.obj
+end
+
+-- Set the receiver to pass objects to.
+function Layer:receiver(o)
+ self.obj.recv, self.obj.ctx = o:receive()
+ self._receiver = o
+end
+
+-- Return the C functions and context for producing objects.
+function Layer:produce()
+ return C.filter_layer_producer(self.obj), self.obj
+end
+
+-- Set the producer to get objects from.
+function Layer:producer(o)
+ self.obj.prod, self.obj.prod_ctx = o:produce()
+ self._producer = o
+end
+
+-- dnsjit.core.object.pcap (3),
+-- dnsjit.core.object.ether (3),
+-- dnsjit.core.object.null (3),
+-- dnsjit.core.object.loop (3),
+-- dnsjit.core.object.linuxsll (3),
+-- dnsjit.core.object.ieee802 (3),
+-- dnsjit.core.object.gre (3),
+-- dnsjit.core.object.ip (3),
+-- dnsjit.core.object.ip6 (3),
+-- dnsjit.core.object.icmp (3),
+-- dnsjit.core.object.icmp6 (3),
+-- dnsjit.core.object.udp (3),
+-- dnsjit.core.object.tcp (3),
+-- dnsjit.core.object.payload (3)
+return Layer
diff --git a/include/dnsjit/filter/split.c b/include/dnsjit/filter/split.c
new file mode 100644
index 0000000..dccf38a
--- /dev/null
+++ b/include/dnsjit/filter/split.c
@@ -0,0 +1,114 @@
+/*
+ * 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 "filter/split.h"
+#include "core/assert.h"
+
+static core_log_t _log = LOG_T_INIT("filter.split");
+static filter_split_t _defaults = {
+ LOG_T_INIT_OBJ("filter.split"),
+ FILTER_SPLIT_MODE_ROUNDROBIN, 0, 0, 0
+};
+
+core_log_t* filter_split_log()
+{
+ return &_log;
+}
+
+void filter_split_init(filter_split_t* self)
+{
+ mlassert_self();
+
+ *self = _defaults;
+}
+
+void filter_split_destroy(filter_split_t* self)
+{
+ filter_split_recv_t* r;
+ mlassert_self();
+
+ if (self->recv_last)
+ self->recv_last->next = 0;
+ while ((r = self->recv_first)) {
+ self->recv_first = r->next;
+ free(r);
+ }
+}
+
+void filter_split_add(filter_split_t* self, core_receiver_t recv, void* ctx)
+{
+ filter_split_recv_t* r;
+ mlassert_self();
+ lassert(recv, "recv is nil");
+
+ lfatal_oom(r = malloc(sizeof(filter_split_recv_t)));
+ r->recv = recv;
+ r->ctx = ctx;
+
+ if (self->recv_last) {
+ self->recv_last->next = r;
+ r->next = self->recv_first;
+ self->recv_first = r;
+ } else {
+ self->recv_first = self->recv = self->recv_last = r;
+ r->next = r;
+ }
+}
+
+static void _roundrobin(filter_split_t* self, const core_object_t* obj)
+{
+ mlassert_self();
+
+ self->recv->recv(self->recv->ctx, obj);
+ self->recv = self->recv->next;
+}
+
+static void _sendall(filter_split_t* self, const core_object_t* obj)
+{
+ filter_split_recv_t* r;
+ mlassert_self();
+
+ for (r = self->recv_first; r; r = r->next) {
+ r->recv(r->ctx, obj);
+ if (r == self->recv_last)
+ break;
+ }
+}
+
+core_receiver_t filter_split_receiver(filter_split_t* self)
+{
+ mlassert_self();
+
+ if (!self->recv) {
+ lfatal("no receiver(s) set");
+ }
+
+ switch (self->mode) {
+ case FILTER_SPLIT_MODE_ROUNDROBIN:
+ return (core_receiver_t)_roundrobin;
+ case FILTER_SPLIT_MODE_SENDALL:
+ return (core_receiver_t)_sendall;
+ default:
+ lfatal("invalid split mode");
+ }
+ return 0;
+}
diff --git a/include/dnsjit/filter/split.h b/include/dnsjit/filter/split.h
new file mode 100644
index 0000000..2b0be09
--- /dev/null
+++ b/include/dnsjit/filter/split.h
@@ -0,0 +1,29 @@
+/*
+ * 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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+
+#ifndef __dnsjit_filter_split_h
+#define __dnsjit_filter_split_h
+
+#include <dnsjit/filter/split.hh>
+
+#endif
diff --git a/include/dnsjit/filter/split.hh b/include/dnsjit/filter/split.hh
new file mode 100644
index 0000000..2aba2ae
--- /dev/null
+++ b/include/dnsjit/filter/split.hh
@@ -0,0 +1,50 @@
+/*
+ * 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")
+
+typedef enum filter_split_mode {
+ FILTER_SPLIT_MODE_ROUNDROBIN,
+ FILTER_SPLIT_MODE_SENDALL
+} filter_split_mode_t;
+
+typedef struct filter_split_recv filter_split_recv_t;
+struct filter_split_recv {
+ filter_split_recv_t* next;
+ core_receiver_t recv;
+ void* ctx;
+};
+
+typedef struct filter_split {
+ core_log_t _log;
+ filter_split_mode_t mode;
+ filter_split_recv_t* recv_first;
+ filter_split_recv_t* recv;
+ filter_split_recv_t* recv_last;
+} filter_split_t;
+
+core_log_t* filter_split_log();
+
+void filter_split_init(filter_split_t* self);
+void filter_split_destroy(filter_split_t* self);
+void filter_split_add(filter_split_t* self, core_receiver_t recv, void* ctx);
+
+core_receiver_t filter_split_receiver(filter_split_t* self);
diff --git a/include/dnsjit/filter/split.lua b/include/dnsjit/filter/split.lua
new file mode 100644
index 0000000..d9aec4f
--- /dev/null
+++ b/include/dnsjit/filter/split.lua
@@ -0,0 +1,80 @@
+-- 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.filter.split
+-- Passthrough to other receivers in various ways
+-- local filter = require("dnsjit.filter.split").new()
+-- filter.receiver(...)
+-- filter.receiver(...)
+-- filter.receiver(...)
+-- input.receiver(filter)
+--
+-- Filter to pass objects to others in various ways.
+module(...,package.seeall)
+
+require("dnsjit.filter.split_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "filter_split_t"
+local filter_split_t = ffi.typeof(t_name)
+local Split = {}
+
+-- Create a new Split filter.
+function Split.new()
+ local self = {
+ receivers = {},
+ obj = filter_split_t(),
+ }
+ C.filter_split_init(self.obj)
+ ffi.gc(self.obj, C.filter_split_destroy)
+ return setmetatable(self, { __index = Split })
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Split:log()
+ if self == nil then
+ return C.filter_split_log()
+ end
+ return self.obj._log
+end
+
+-- Set the passthrough mode to round robin (default mode).
+function Split:roundrobin()
+ self.obj.mode = "FILTER_SPLIT_MODE_ROUNDROBIN"
+end
+
+-- Set the passthrough mode to send to all receivers.
+function Split:sendall()
+ self.obj.mode = "FILTER_SPLIT_MODE_SENDALL"
+end
+
+-- Return the C functions and context for receiving objects.
+function Split:receive()
+ return C.filter_split_receiver(self.obj), self.obj
+end
+
+-- Set the receiver to pass objects to, this can be called multiple times to
+-- set addtional receivers.
+function Split:receiver(o)
+ local recv, ctx = o:receive()
+ C.filter_split_add(self.obj, recv, ctx)
+ table.insert(self.receivers, o)
+end
+
+return Split
diff --git a/include/dnsjit/filter/timing.c b/include/dnsjit/filter/timing.c
new file mode 100644
index 0000000..bf4f865
--- /dev/null
+++ b/include/dnsjit/filter/timing.c
@@ -0,0 +1,557 @@
+/*
+ * 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 "filter/timing.h"
+#include "core/assert.h"
+#include "core/timespec.h"
+#include "core/object/pcap.h"
+
+#include <time.h>
+#include <sys/time.h>
+
+#define N1e9 1000000000
+
+typedef struct _filter_timing {
+ filter_timing_t pub;
+
+ struct timespec diff;
+ core_timespec_t last_pkthdr_ts;
+ struct timespec last_ts;
+ struct timespec first_ts;
+ void (*timing_callback)(filter_timing_t*, const core_object_pcap_t*);
+ struct timespec mod_ts;
+ size_t counter;
+} _filter_timing_t;
+
+static core_log_t _log = LOG_T_INIT("filter.timing");
+static filter_timing_t _defaults = {
+ LOG_T_INIT_OBJ("filter.timing"),
+ 0, 0,
+ TIMING_MODE_KEEP, 0, 0, 0, 0, 0.0, 0,
+ 0, 0
+};
+
+#define _self ((_filter_timing_t*)self)
+
+core_log_t* filter_timing_log()
+{
+ return &_log;
+}
+
+static void _keep(filter_timing_t* self, const core_object_pcap_t* pkt)
+{
+#if HAVE_CLOCK_NANOSLEEP
+ struct timespec to = {
+ _self->diff.tv_sec + pkt->ts.sec,
+ _self->diff.tv_nsec + pkt->ts.nsec
+ };
+ int ret = EINTR;
+
+ if (to.tv_nsec >= N1e9) {
+ to.tv_sec += 1;
+ to.tv_nsec -= N1e9;
+ } else if (to.tv_nsec < 0) {
+ to.tv_sec -= 1;
+ to.tv_nsec += N1e9;
+ }
+
+ while (ret) {
+ ldebug("keep mode, sleep to %ld.%09ld", to.tv_sec, to.tv_nsec);
+ ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &to, 0);
+ if (ret && ret != EINTR) {
+ lfatal("clock_nanosleep(%ld.%09ld) %d", to.tv_sec, to.tv_nsec, ret);
+ }
+ }
+#elif HAVE_NANOSLEEP
+ struct timespec diff = {
+ pkt->ts.sec - _self->last_pkthdr_ts.sec,
+ pkt->ts.nsec - _self->last_pkthdr_ts.nsec
+ };
+ int ret = EINTR;
+
+ if (diff.tv_nsec >= N1e9) {
+ diff.tv_sec += 1;
+ diff.tv_nsec -= N1e9;
+ } else if (diff.tv_nsec < 0) {
+ diff.tv_sec -= 1;
+ diff.tv_nsec += N1e9;
+ }
+
+ if (diff.tv_sec > -1 && diff.tv_nsec > -1) {
+ while (ret) {
+ ldebug("keep mode, sleep for %ld.%09ld", diff.tv_sec, diff.tv_nsec);
+ if ((ret = nanosleep(&diff, &diff))) {
+ ret = errno;
+ if (ret != EINTR) {
+ lfatal("nanosleep(%ld.%09ld) %d", diff.tv_sec, diff.tv_nsec, ret);
+ }
+ }
+ }
+ }
+
+ _self->last_pkthdr_ts = pkt->ts;
+#endif
+}
+
+static void _increase(filter_timing_t* self, const core_object_pcap_t* pkt)
+{
+ struct timespec diff = {
+ pkt->ts.sec - _self->last_pkthdr_ts.sec,
+ pkt->ts.nsec - _self->last_pkthdr_ts.nsec
+ };
+ int ret = EINTR;
+
+ if (diff.tv_nsec >= N1e9) {
+ diff.tv_sec += 1;
+ diff.tv_nsec -= N1e9;
+ } else if (diff.tv_nsec < 0) {
+ diff.tv_sec -= 1;
+ diff.tv_nsec += N1e9;
+ }
+
+ diff.tv_sec += _self->mod_ts.tv_sec;
+ diff.tv_nsec += _self->mod_ts.tv_nsec;
+ if (diff.tv_nsec >= N1e9) {
+ diff.tv_sec += 1;
+ diff.tv_nsec -= N1e9;
+ }
+
+ if (diff.tv_sec > -1 && diff.tv_nsec > -1) {
+#if HAVE_CLOCK_NANOSLEEP
+ struct timespec to = {
+ _self->last_ts.tv_sec + diff.tv_sec,
+ _self->last_ts.tv_nsec + diff.tv_nsec
+ };
+
+ if (to.tv_nsec >= N1e9) {
+ to.tv_sec += 1;
+ to.tv_nsec -= N1e9;
+ } else if (to.tv_nsec < 0) {
+ to.tv_sec -= 1;
+ to.tv_nsec += N1e9;
+ }
+
+ while (ret) {
+ ldebug("increase mode, sleep to %ld.%09ld", to.tv_sec, to.tv_nsec);
+ ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &to, 0);
+ if (ret && ret != EINTR) {
+ lfatal("clock_nanosleep(%ld.%09ld) %d", to.tv_sec, to.tv_nsec, ret);
+ }
+ }
+#elif HAVE_NANOSLEEP
+ while (ret) {
+ ldebug("increase mode, sleep for %ld.%09ld", diff.tv_sec, diff.tv_nsec);
+ if ((ret = nanosleep(&diff, &diff))) {
+ ret = errno;
+ if (ret != EINTR) {
+ lfatal("nanosleep(%ld.%09ld) %d", diff.tv_sec, diff.tv_nsec, ret);
+ }
+ }
+ }
+#endif
+ }
+
+ _self->last_pkthdr_ts = pkt->ts;
+
+#if HAVE_CLOCK_NANOSLEEP
+ if (clock_gettime(CLOCK_MONOTONIC, &_self->last_ts)) {
+ lfatal("clock_gettime()");
+ }
+#endif
+}
+
+static void _reduce(filter_timing_t* self, const core_object_pcap_t* pkt)
+{
+ struct timespec diff = {
+ pkt->ts.sec - _self->last_pkthdr_ts.sec,
+ pkt->ts.nsec - _self->last_pkthdr_ts.nsec
+ };
+ int ret = EINTR;
+
+ if (diff.tv_nsec >= N1e9) {
+ diff.tv_sec += 1;
+ diff.tv_nsec -= N1e9;
+ } else if (diff.tv_nsec < 0) {
+ diff.tv_sec -= 1;
+ diff.tv_nsec += N1e9;
+ }
+
+ diff.tv_sec -= _self->mod_ts.tv_sec;
+ diff.tv_nsec -= _self->mod_ts.tv_nsec;
+ if (diff.tv_nsec < 0) {
+ diff.tv_sec -= 1;
+ diff.tv_nsec += N1e9;
+ }
+
+ if (diff.tv_sec > -1 && diff.tv_nsec > -1) {
+#if HAVE_CLOCK_NANOSLEEP
+ struct timespec to = {
+ _self->last_ts.tv_sec + diff.tv_sec,
+ _self->last_ts.tv_nsec + diff.tv_nsec
+ };
+
+ if (to.tv_nsec >= N1e9) {
+ to.tv_sec += 1;
+ to.tv_nsec -= N1e9;
+ } else if (to.tv_nsec < 0) {
+ to.tv_sec -= 1;
+ to.tv_nsec += N1e9;
+ }
+
+ while (ret) {
+ ldebug("reduce mode, sleep to %ld.%09ld", to.tv_sec, to.tv_nsec);
+ ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &to, 0);
+ if (ret && ret != EINTR) {
+ lfatal("clock_nanosleep(%ld.%09ld) %d", to.tv_sec, to.tv_nsec, ret);
+ }
+ }
+#elif HAVE_NANOSLEEP
+ while (ret) {
+ ldebug("reduce mode, sleep for %ld.%09ld", diff.tv_sec, diff.tv_nsec);
+ if ((ret = nanosleep(&diff, &diff))) {
+ ret = errno;
+ if (ret != EINTR) {
+ lfatal("nanosleep(%ld.%09ld) %d", diff.tv_sec, diff.tv_nsec, ret);
+ }
+ }
+ }
+#endif
+ }
+
+ _self->last_pkthdr_ts = pkt->ts;
+
+#if HAVE_CLOCK_NANOSLEEP
+ if (clock_gettime(CLOCK_MONOTONIC, &_self->last_ts)) {
+ lfatal("clock_gettime()");
+ }
+#endif
+}
+
+static void _multiply(filter_timing_t* self, const core_object_pcap_t* pkt)
+{
+ struct timespec diff = {
+ pkt->ts.sec - _self->last_pkthdr_ts.sec,
+ pkt->ts.nsec - _self->last_pkthdr_ts.nsec
+ };
+ int ret = EINTR;
+
+ if (diff.tv_nsec >= N1e9) {
+ diff.tv_sec += 1;
+ diff.tv_nsec -= N1e9;
+ } else if (diff.tv_nsec < 0) {
+ diff.tv_sec -= 1;
+ diff.tv_nsec += N1e9;
+ }
+
+ diff.tv_sec = (time_t)((float)diff.tv_sec * self->mul);
+ diff.tv_nsec = (long)((float)diff.tv_nsec * self->mul);
+ if (diff.tv_nsec >= N1e9) {
+ diff.tv_sec += diff.tv_nsec / N1e9;
+ diff.tv_nsec %= N1e9;
+ }
+
+ if (diff.tv_sec > -1 && diff.tv_nsec > -1) {
+#if HAVE_CLOCK_NANOSLEEP
+ struct timespec to = {
+ _self->last_ts.tv_sec + diff.tv_sec,
+ _self->last_ts.tv_nsec + diff.tv_nsec
+ };
+
+ if (to.tv_nsec >= N1e9) {
+ to.tv_sec += 1;
+ to.tv_nsec -= N1e9;
+ } else if (to.tv_nsec < 0) {
+ to.tv_sec -= 1;
+ to.tv_nsec += N1e9;
+ }
+
+ while (ret) {
+ ldebug("multiply mode, sleep to %ld.%09ld", to.tv_sec, to.tv_nsec);
+ ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &to, 0);
+ if (ret && ret != EINTR) {
+ lfatal("clock_nanosleep(%ld.%09ld) %d", to.tv_sec, to.tv_nsec, ret);
+ }
+ }
+#elif HAVE_NANOSLEEP
+ while (ret) {
+ ldebug("multiply mode, sleep for %ld.%09ld", diff.tv_sec, diff.tv_nsec);
+ if ((ret = nanosleep(&diff, &diff))) {
+ ret = errno;
+ if (ret != EINTR) {
+ lfatal("nanosleep(%ld.%09ld) %d", diff.tv_sec, diff.tv_nsec, ret);
+ }
+ }
+ }
+#endif
+ }
+
+ _self->last_pkthdr_ts = pkt->ts;
+
+#if HAVE_CLOCK_NANOSLEEP
+ if (clock_gettime(CLOCK_MONOTONIC, &_self->last_ts)) {
+ lfatal("clock_gettime()");
+ }
+#endif
+}
+
+static void _fixed(filter_timing_t* self, const core_object_pcap_t* pkt)
+{
+ struct timespec diff = {
+ _self->mod_ts.tv_sec,
+ _self->mod_ts.tv_nsec
+ };
+ int ret = EINTR;
+
+ if (diff.tv_sec > -1 && diff.tv_nsec > -1) {
+#if HAVE_CLOCK_NANOSLEEP
+ struct timespec to = {
+ _self->last_ts.tv_sec + diff.tv_sec,
+ _self->last_ts.tv_nsec + diff.tv_nsec
+ };
+
+ if (to.tv_nsec >= N1e9) {
+ to.tv_sec += 1;
+ to.tv_nsec -= N1e9;
+ } else if (to.tv_nsec < 0) {
+ to.tv_sec -= 1;
+ to.tv_nsec += N1e9;
+ }
+
+ while (ret) {
+ ldebug("fixed mode, sleep to %ld.%09ld", to.tv_sec, to.tv_nsec);
+ ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &to, 0);
+ if (ret && ret != EINTR) {
+ lfatal("clock_nanosleep(%ld.%09ld) %d", to.tv_sec, to.tv_nsec, ret);
+ }
+ }
+#elif HAVE_NANOSLEEP
+ while (ret) {
+ ldebug("fixed mode, sleep for %ld.%09ld", diff.tv_sec, diff.tv_nsec);
+ if ((ret = nanosleep(&diff, &diff))) {
+ ret = errno;
+ if (ret != EINTR) {
+ lfatal("nanosleep(%ld.%09ld) %d", diff.tv_sec, diff.tv_nsec, ret);
+ }
+ }
+ }
+#endif
+ }
+
+ _self->last_pkthdr_ts = pkt->ts;
+
+#if HAVE_CLOCK_NANOSLEEP
+ if (clock_gettime(CLOCK_MONOTONIC, &_self->last_ts)) {
+ lfatal("clock_gettime()");
+ }
+#endif
+}
+
+#if HAVE_CLOCK_NANOSLEEP
+static inline void _timespec_diff(struct timespec* start, struct timespec* stop,
+ struct timespec* result)
+{
+ if ((stop->tv_nsec - start->tv_nsec) < 0) {
+ mlassert(stop->tv_sec > start->tv_sec, "stop time must be after start time");
+ result->tv_sec = stop->tv_sec - start->tv_sec - 1;
+ result->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000UL;
+ } else {
+ mlassert(stop->tv_sec >= start->tv_sec, "stop time must be after start time");
+ result->tv_sec = stop->tv_sec - start->tv_sec;
+ result->tv_nsec = stop->tv_nsec - start->tv_nsec;
+ }
+}
+
+static void _realtime(filter_timing_t* self, const core_object_pcap_t* pkt)
+{
+ _self->counter++;
+ if (_self->counter >= self->rt_batch) {
+ struct timespec simulated;
+
+ _self->counter = 0;
+ if (clock_gettime(CLOCK_MONOTONIC, &_self->last_ts)) {
+ lfatal("clock_gettime()");
+ }
+
+ // calculate simulated time from packet offsets
+ simulated.tv_sec = pkt->ts.sec;
+ simulated.tv_nsec = pkt->ts.nsec;
+ _timespec_diff(&_self->mod_ts, &simulated, &simulated);
+
+ // calculate real elapsed time from monotonic clock
+ _timespec_diff(&_self->first_ts, &_self->last_ts, &_self->diff);
+
+ linfo("simulated time: %ld.%09lds; real time: %ld.%09lds",
+ simulated.tv_sec, simulated.tv_nsec, _self->diff.tv_sec, _self->diff.tv_nsec);
+
+ if (simulated.tv_sec > _self->diff.tv_sec
+ || (simulated.tv_sec == _self->diff.tv_sec && simulated.tv_nsec > _self->diff.tv_nsec)) {
+ int ret = EINTR;
+ _timespec_diff(&_self->diff, &simulated, &simulated);
+
+ ldebug("sleeping for %ld.%09lds", simulated.tv_sec, simulated.tv_nsec);
+ while (ret) {
+ ret = clock_nanosleep(CLOCK_MONOTONIC, 0, &simulated, 0);
+ if (ret && ret != EINTR) {
+ lfatal("clock_nanosleep(%ld.%09ld) %d", simulated.tv_sec, simulated.tv_nsec, ret);
+ }
+ }
+ } else {
+ // check that real time didn't drift ahead more than specified drift limit
+ _timespec_diff(&simulated, &_self->diff, &_self->diff);
+ if (_self->diff.tv_sec > (self->rt_drift / N1e9)
+ || (_self->diff.tv_sec == (self->rt_drift / N1e9) && _self->diff.tv_nsec >= (self->rt_drift % N1e9))) {
+ lfatal("aborting, real time drifted ahead of simulated time (%ld.%09lds) by %ld.%09lds",
+ simulated.tv_sec, simulated.tv_nsec, _self->diff.tv_sec, _self->diff.tv_nsec);
+ }
+ }
+ }
+}
+#endif
+
+static void _init(filter_timing_t* self, const core_object_pcap_t* pkt)
+{
+#if HAVE_CLOCK_NANOSLEEP
+ if (clock_gettime(CLOCK_MONOTONIC, &_self->last_ts)) {
+ lfatal("clock_gettime()");
+ }
+ _self->first_ts = _self->last_ts;
+ _self->diff = _self->last_ts;
+ _self->diff.tv_sec -= pkt->ts.sec;
+ _self->diff.tv_nsec -= pkt->ts.nsec;
+ ldebug("init with clock_nanosleep() now is %ld.%09ld, diff of first pkt %ld.%09ld",
+ _self->last_ts.tv_sec, _self->last_ts.tv_nsec,
+ _self->diff.tv_sec, _self->diff.tv_nsec);
+#elif HAVE_NANOSLEEP
+ ldebug("init with nanosleep()");
+#else
+#error "No clock_nanosleep() or nanosleep(), can not continue"
+#endif
+
+ _self->last_pkthdr_ts = pkt->ts;
+
+ switch (self->mode) {
+ case TIMING_MODE_KEEP:
+ ldebug("init mode keep");
+ _self->timing_callback = _keep;
+ break;
+ case TIMING_MODE_INCREASE:
+ _self->timing_callback = _increase;
+ _self->mod_ts.tv_sec = self->inc / N1e9;
+ _self->mod_ts.tv_nsec = self->inc % N1e9;
+ ldebug("init mode increase by %ld.%09ld", _self->mod_ts.tv_sec, _self->mod_ts.tv_nsec);
+ break;
+ case TIMING_MODE_REDUCE:
+ _self->timing_callback = _reduce;
+ _self->mod_ts.tv_sec = self->red / N1e9;
+ _self->mod_ts.tv_nsec = self->red % N1e9;
+ ldebug("init mode reduce by %ld.%09ld", _self->mod_ts.tv_sec, _self->mod_ts.tv_nsec);
+ break;
+ case TIMING_MODE_MULTIPLY:
+ _self->timing_callback = _multiply;
+ ldebug("init mode multiply by %f", self->mul);
+ break;
+ case TIMING_MODE_FIXED:
+ _self->timing_callback = _fixed;
+ _self->mod_ts.tv_sec = self->fixed / N1e9;
+ _self->mod_ts.tv_nsec = self->fixed % N1e9;
+ ldebug("init mode fixed by %ld.%09ld", _self->mod_ts.tv_sec, _self->mod_ts.tv_nsec);
+ break;
+ case TIMING_MODE_REALTIME:
+#if HAVE_CLOCK_NANOSLEEP
+ ldebug("init mode realtime");
+ _self->timing_callback = _realtime;
+ _self->counter = 0;
+ _self->mod_ts.tv_sec = pkt->ts.sec;
+ _self->mod_ts.tv_nsec = pkt->ts.nsec;
+#else
+ lfatal("realtime mode requires clock_nanosleep()");
+#endif
+ break;
+ default:
+ lfatal("invalid timing mode %d", self->mode);
+ }
+}
+
+filter_timing_t* filter_timing_new()
+{
+ filter_timing_t* self;
+ mlfatal_oom(self = malloc(sizeof(_filter_timing_t)));
+ *self = _defaults;
+ _self->timing_callback = _init;
+
+ return self;
+}
+
+void filter_timing_free(filter_timing_t* self)
+{
+ mlassert_self();
+ free(self);
+}
+
+static void _receive(filter_timing_t* self, const core_object_t* obj)
+{
+ mlassert_self();
+ lassert(obj, "obj is nil");
+
+ if (obj->obj_type != CORE_OBJECT_PCAP) {
+ lfatal("obj is not CORE_OBJECT_PCAP");
+ }
+
+ _self->timing_callback(self, (core_object_pcap_t*)obj);
+ self->recv(self->ctx, obj);
+}
+
+core_receiver_t filter_timing_receiver(filter_timing_t* self)
+{
+ mlassert_self();
+
+ if (!self->recv) {
+ lfatal("no receiver set");
+ }
+
+ return (core_receiver_t)_receive;
+}
+
+static const core_object_t* _produce(filter_timing_t* self)
+{
+ const core_object_t* obj;
+ mlassert_self();
+
+ obj = self->prod(self->prod_ctx);
+ if (!obj || obj->obj_type != CORE_OBJECT_PCAP) {
+ return 0;
+ }
+
+ _self->timing_callback(self, (core_object_pcap_t*)obj);
+ return obj;
+}
+
+core_producer_t filter_timing_producer(filter_timing_t* self)
+{
+ mlassert_self();
+
+ if (!self->prod) {
+ lfatal("no producer set");
+ }
+
+ return (core_producer_t)_produce;
+}
diff --git a/include/dnsjit/filter/timing.h b/include/dnsjit/filter/timing.h
new file mode 100644
index 0000000..283155c
--- /dev/null
+++ b/include/dnsjit/filter/timing.h
@@ -0,0 +1,30 @@
+/*
+ * 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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+#include <dnsjit/core/producer.h>
+
+#ifndef __dnsjit_filter_timing_h
+#define __dnsjit_filter_timing_h
+
+#include <dnsjit/filter/timing.hh>
+
+#endif
diff --git a/include/dnsjit/filter/timing.hh b/include/dnsjit/filter/timing.hh
new file mode 100644
index 0000000..8615907
--- /dev/null
+++ b/include/dnsjit/filter/timing.hh
@@ -0,0 +1,52 @@
+/*
+ * 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.timespec_h")
+
+typedef struct filter_timing {
+ core_log_t _log;
+ core_receiver_t recv;
+ void* ctx;
+ enum {
+ TIMING_MODE_KEEP = 0,
+ TIMING_MODE_INCREASE = 1,
+ TIMING_MODE_REDUCE = 2,
+ TIMING_MODE_MULTIPLY = 3,
+ TIMING_MODE_FIXED = 4,
+ TIMING_MODE_REALTIME = 5
+ } mode;
+ size_t inc, red, fixed, rt_batch;
+ float mul;
+ uint64_t rt_drift;
+
+ core_producer_t prod;
+ void* prod_ctx;
+} filter_timing_t;
+
+core_log_t* filter_timing_log();
+
+filter_timing_t* filter_timing_new();
+void filter_timing_free(filter_timing_t* self);
+
+core_receiver_t filter_timing_receiver(filter_timing_t* self);
+core_producer_t filter_timing_producer(filter_timing_t* self);
diff --git a/include/dnsjit/filter/timing.lua b/include/dnsjit/filter/timing.lua
new file mode 100644
index 0000000..cab9574
--- /dev/null
+++ b/include/dnsjit/filter/timing.lua
@@ -0,0 +1,123 @@
+-- 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.filter.timing
+-- Filter to pass objects to the next receiver based on timing between packets
+-- local filter = require("dnsjit.filter.timing").new()
+-- ...
+-- filter:receiver(...)
+--
+-- Filter to manipulate processing so it simulates the actual timing when
+-- packets arrived or to delay processing.
+module(...,package.seeall)
+
+require("dnsjit.filter.timing_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local Timing = {}
+
+-- Create a new Timing filter.
+function Timing.new()
+ local self = {
+ _receiver = nil,
+ obj = C.filter_timing_new(),
+ }
+ ffi.gc(self.obj, C.filter_timing_free)
+ return setmetatable(self, { __index = Timing })
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Timing:log()
+ if self == nil then
+ return C.filter_timing_log()
+ end
+ return self.obj._log
+end
+
+-- Set the timing mode to keep the timing between packets.
+function Timing:keep()
+ self.obj.mode = "TIMING_MODE_KEEP"
+end
+
+-- Set the timing mode to increase the timing between packets by the given
+-- number of nanoseconds.
+function Timing:increase(ns)
+ self.obj.mode = "TIMING_MODE_INCREASE"
+ self.obj.inc = ns
+end
+
+-- Set the timing mode to reduce the timing between packets by the given
+-- number of nanoseconds.
+function Timing:reduce(ns)
+ self.obj.mode = "TIMING_MODE_REDUCE"
+ self.obj.red = ns
+end
+
+-- Set the timing mode to multiply the timing between packets by the given
+-- factor (float/double).
+function Timing:multiply(factor)
+ self.obj.mode = "TIMING_MODE_MULTIPLY"
+ self.obj.mul = factor
+end
+
+-- Set the timing mode to a fixed number of nanoseconds between packets.
+function Timing:fixed(ns)
+ self.obj.mode = "TIMING_MODE_FIXED"
+ self.obj.fixed = ns
+end
+
+-- Set the timing mode to simulate the timing of packets in realtime.
+-- Packets are processed in batches of given size (default 128) before
+-- adjusting time. Aborts if real time drifts ahead more than given
+-- number of seconds (default 1.0s).
+function Timing:realtime(drift, batch_size)
+ self.obj.mode = "TIMING_MODE_REALTIME"
+ if drift == nil then
+ drift = 1
+ end
+ if batch_size == nil then
+ batch_size = 128
+ end
+ self.obj.rt_batch = batch_size
+ self.obj.rt_drift = math.floor(drift * 1000000000)
+end
+
+-- Return the C functions and context for receiving objects.
+function Timing:receive()
+ return C.filter_timing_receiver(), self.obj
+end
+
+-- Set the receiver to pass objects to.
+function Timing:receiver(o)
+ self.obj.recv, self.obj.ctx = o:receive()
+ self._receiver = o
+end
+
+-- Return the C functions and context for producing objects.
+function Timing:produce()
+ return C.filter_timing_producer(self.obj), self.obj
+end
+
+-- Set the producer to get objects from.
+function Timing:producer(o)
+ self.obj.prod, self.obj.prod_ctx = o:produce()
+ self._producer = o
+end
+
+return Timing
diff --git a/include/dnsjit/gen-compat.lua b/include/dnsjit/gen-compat.lua
new file mode 100644
index 0000000..65c40ec
--- /dev/null
+++ b/include/dnsjit/gen-compat.lua
@@ -0,0 +1,34 @@
+for line in io.lines("config.h") do
+ local n, s = line:match("define SIZEOF_(%S*) (%d+)")
+ if n and s then
+ if n:match("^PTHREAD") or n:match("^CK_") or n:match("^GNUTLS_") then
+ s = math.ceil(s / 8)
+ print("#if !defined(SIZEOF_"..n..") || SIZEOF_"..n.." == 0")
+ print("#error \""..n.." is undefined or zero\"")
+ print("#endif")
+ n = n:lower()
+ print("typedef struct "..n:sub(1,-3).." { uint64_t a["..s.."]; } "..n..";")
+ elseif n:match("^STRUCT") then
+ n = n:match("^STRUCT_(%S*)")
+ if n == "SOCKADDR_STORAGE" or n == "POLLFD" then
+ print("#if !defined(SIZEOF_STRUCT_"..n..") || SIZEOF_STRUCT_"..n.." == 0")
+ print("#error \""..n.." is undefined or zero\"")
+ print("#endif")
+ n = n:lower()
+ print("struct "..n.." { uint8_t a["..s.."]; };")
+ end
+ end
+ end
+end
+code, err = pcall(function()
+ local ffi = require("ffi")
+ ffi.cdef[[
+ ssize_t dummy;
+ ]]
+end)
+if code then
+ print("#include <unistd.h>")
+ print("typedef ssize_t luajit_ssize_t;")
+else
+ print("typedef long luajit_ssize_t;")
+end
diff --git a/include/dnsjit/gen-errno.sh b/include/dnsjit/gen-errno.sh
new file mode 100755
index 0000000..f9a81da
--- /dev/null
+++ b/include/dnsjit/gen-errno.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+includes="/usr/include/errno.h"
+if [ -f /usr/include/sys/errno.h ]; then
+ includes="$includes /usr/include/sys/errno.h"
+fi
+if [ -f /usr/include/asm-generic/errno.h ]; then
+ includes="$includes /usr/include/asm-generic/errno.h"
+fi
+if [ -f /usr/include/asm-generic/errno-base.h ]; then
+ includes="$includes /usr/include/asm-generic/errno-base.h"
+fi
+
+echo 'const char* core_log_errstr(int err)
+{
+ switch (err) {'
+
+egrep -Eh '^#define[ ]+E\w+[ ]+[0-9]+' $includes |
+ grep -v ELAST |
+ awk '{print $2}' |
+ sort -u |
+ awk '{print "#ifdef " $1 "\n case " $1 ":\n return \"" $1 "\";\n#endif"}'
+
+echo ' default:
+ break;
+ }
+ return "UNKNOWN";
+}'
diff --git a/include/dnsjit/gen-manpage.lua b/include/dnsjit/gen-manpage.lua
new file mode 100644
index 0000000..e285b1f
--- /dev/null
+++ b/include/dnsjit/gen-manpage.lua
@@ -0,0 +1,126 @@
+print[[
+.\" 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/>.
+.\"]]
+
+sh_syn = false
+sh_desc = false
+ss_func = false
+doc = {}
+funcs = {}
+for line in io.lines(arg[1]) do
+ if line:match("^[-][-]") then
+ table.insert(doc, line:sub(4))
+ elseif line:match("^module") then
+ if table.maxn(doc) < 2 then
+ error("Minimum required module doc missing")
+ end
+ print(".TH "..doc[1].." 3 \"@PACKAGE_VERSION@\" \"dnsjit\"")
+ print(".SH NAME")
+ print(doc[1].." \\- "..doc[2])
+ n, line = next(doc, 2)
+ if n and n > 2 then
+ local n2 = n
+ while line and line > "" do
+ if not sh_syn then
+ print(".SH SYNOPSIS")
+ sh_syn = true
+ end
+ if line == "." then
+ line = ""
+ end
+ print(line)
+ n2 = n
+ n, line = next(doc, n)
+ end
+ n, line = next(doc, n)
+ if n and n > n2 then
+ while line and line > "" do
+ if not sh_desc then
+ print(".SH DESCRIPTION")
+ sh_desc = true
+ end
+ if line == "." then
+ line = ""
+ end
+ print(line)
+ n, line = next(doc, n)
+ end
+ end
+ end
+ elseif line:match("^function") then
+ if table.maxn(doc) > 0 then
+ if not ss_func then
+ if not sh_desc then
+ print(".SH DESCRIPTION")
+ sh_desc = true
+ end
+ print(".SS Functions")
+ ss_func = true
+ end
+ print(".TP")
+ local fn,fd = line:match("(%S+)(%(.+)")
+ if fn and fd then
+ print(".BR "..fn.." \""..fd.."\"")
+ else
+ print(".B "..line:sub(10))
+ end
+ for _, line in pairs(doc) do
+ if line == "." then
+ line = ""
+ end
+ print(line)
+ end
+ end
+ elseif line:match("^return") then
+ if table.maxn(doc) > 0 then
+ print(".SH SEE ALSO")
+ for _, line in pairs(doc) do
+ print(".BR "..line)
+ end
+ end
+ else
+ doc = {}
+ end
+end
+
+print[[
+.SH AUTHORS and CONTRIBUTORS
+Jerry Lundström (DNS-OARC),
+Tomáš Křížek (CZ.NIC),
+Petr Špaček (ISC)
+.LP
+Maintained by DNS-OARC
+.LP
+.RS
+.I https://www.dns-oarc.net/
+.RE
+.LP
+.SH BUGS
+For issues and feature requests please use:
+.LP
+.RS
+\fI@PACKAGE_URL@\fP
+.RE
+.LP
+For question and help please use:
+.LP
+.RS
+\fI@PACKAGE_BUGREPORT@\fP
+.RE
+.LP]]
diff --git a/include/dnsjit/globals.c b/include/dnsjit/globals.c
new file mode 100644
index 0000000..3f85c51
--- /dev/null
+++ b/include/dnsjit/globals.c
@@ -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/>.
+ */
+
+#include "config.h"
+
+#include "globals.h"
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+void dnsjit_globals(lua_State* L)
+{
+#ifdef PACKAGE_VERSION
+ lua_pushstring(L, PACKAGE_VERSION);
+#elif defined(VERSION)
+ lua_pushstring(L, VERSION);
+#else
+#error "No PACKAGE_VERSION or VERSION defined"
+#endif
+ lua_setglobal(L, "DNSJIT_VERSION");
+
+ lua_pushinteger(L, PACKAGE_MAJOR_VERSION);
+ lua_setglobal(L, "DNSJIT_MAJOR_VERSION");
+ lua_pushinteger(L, PACKAGE_MINOR_VERSION);
+ lua_setglobal(L, "DNSJIT_MINOR_VERSION");
+ lua_pushinteger(L, PACKAGE_PATCH_VERSION);
+ lua_setglobal(L, "DNSJIT_PATCH_VERSION");
+
+#ifdef PACKAGE_BUGREPORT
+ lua_pushstring(L, PACKAGE_BUGREPORT);
+#else
+ lua_pushstring(L, "none");
+#endif
+ lua_setglobal(L, "DNSJIT_BUGREPORT");
+
+#ifdef PACKAGE_URL
+ lua_pushstring(L, PACKAGE_URL);
+#else
+ lua_pushstring(L, "none");
+#endif
+ lua_setglobal(L, "DNSJIT_URL");
+}
diff --git a/include/dnsjit/globals.h b/include/dnsjit/globals.h
new file mode 100644
index 0000000..48e7e68
--- /dev/null
+++ b/include/dnsjit/globals.h
@@ -0,0 +1,28 @@
+/*
+ * 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/>.
+ */
+
+#ifndef __dnsjit_globals_h
+#define __dnsjit_globals_h
+
+#include <lua.h>
+
+void dnsjit_globals(lua_State* L);
+
+#endif
diff --git a/include/dnsjit/input.lua b/include/dnsjit/input.lua
new file mode 100644
index 0000000..f791f17
--- /dev/null
+++ b/include/dnsjit/input.lua
@@ -0,0 +1,30 @@
+-- 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
+-- Input modules for dnsjit
+--
+-- Input modules used to read DNS messages in various ways.
+module(...,package.seeall)
+
+-- dnsjit.input.fpcap (3),
+-- dnsjit.input.mmpcap (3),
+-- dnsjit.input.pcap (3),
+-- dnsjit.input.zero (3),
+-- dnsjit.input.zpcap (3)
+return
diff --git a/include/dnsjit/input/fpcap.c b/include/dnsjit/input/fpcap.c
new file mode 100644
index 0000000..6804608
--- /dev/null
+++ b/include/dnsjit/input/fpcap.c
@@ -0,0 +1,349 @@
+/*
+ * 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 <fcntl.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,
+ 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 _POSIX_C_SOURCE >= 200112L || defined(__FreeBSD__)
+ if (self->use_fadvise) {
+ int err = posix_fadvise(fileno((FILE*)self->file), 0, 0, POSIX_FADV_SEQUENTIAL);
+ if (err) {
+ lcritical("posix_fadvise() failed: %s", core_log_errstr(err));
+ return -2;
+ }
+ }
+#endif
+
+ 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/include/dnsjit/input/fpcap.h b/include/dnsjit/input/fpcap.h
new file mode 100644
index 0000000..4e1e5ee
--- /dev/null
+++ b/include/dnsjit/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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+#include <dnsjit/core/producer.h>
+#include <dnsjit/core/object/pcap.h>
+
+#ifndef __dnsjit_input_fpcap_h
+#define __dnsjit_input_fpcap_h
+
+#include <dnsjit/input/fpcap.hh>
+
+#endif
diff --git a/include/dnsjit/input/fpcap.hh b/include/dnsjit/input/fpcap.hh
new file mode 100644
index 0000000..ab06a71
--- /dev/null
+++ b/include/dnsjit/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, use_fadvise;
+ 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/include/dnsjit/input/fpcap.lua b/include/dnsjit/input/fpcap.lua
new file mode 100644
index 0000000..b50fc11
--- /dev/null
+++ b/include/dnsjit/input/fpcap.lua
@@ -0,0 +1,140 @@
+-- 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
+
+-- Use
+-- .B posix_fadvise()
+-- to indicate sequential reading (if supported), may increase performance.
+-- MUST be called before
+-- .BR open() .
+function Fpcap:fadvise_sequential()
+ self.obj.use_fadvise = 1
+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/include/dnsjit/input/mmpcap.c b/include/dnsjit/input/mmpcap.c
new file mode 100644
index 0000000..4f3ee59
--- /dev/null
+++ b/include/dnsjit/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/include/dnsjit/input/mmpcap.h b/include/dnsjit/input/mmpcap.h
new file mode 100644
index 0000000..b4ae09a
--- /dev/null
+++ b/include/dnsjit/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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+#include <dnsjit/core/producer.h>
+#include <dnsjit/core/object/pcap.h>
+
+#ifndef __dnsjit_input_mmpcap_h
+#define __dnsjit_input_mmpcap_h
+
+#include <dnsjit/input/mmpcap.hh>
+
+#endif
diff --git a/include/dnsjit/input/mmpcap.hh b/include/dnsjit/input/mmpcap.hh
new file mode 100644
index 0000000..27ed35e
--- /dev/null
+++ b/include/dnsjit/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/include/dnsjit/input/mmpcap.lua b/include/dnsjit/input/mmpcap.lua
new file mode 100644
index 0000000..8e28e69
--- /dev/null
+++ b/include/dnsjit/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/include/dnsjit/input/pcap.c b/include/dnsjit/input/pcap.c
new file mode 100644
index 0000000..25d6ee3
--- /dev/null
+++ b/include/dnsjit/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/include/dnsjit/input/pcap.h b/include/dnsjit/input/pcap.h
new file mode 100644
index 0000000..cba5227
--- /dev/null
+++ b/include/dnsjit/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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+#include <dnsjit/core/producer.h>
+#include <dnsjit/core/object/pcap.h>
+
+#ifndef __dnsjit_input_pcap_h
+#define __dnsjit_input_pcap_h
+
+#include <pcap/pcap.h>
+
+#include <dnsjit/input/pcap.hh>
+
+#endif
diff --git a/include/dnsjit/input/pcap.hh b/include/dnsjit/input/pcap.hh
new file mode 100644
index 0000000..a9e0a41
--- /dev/null
+++ b/include/dnsjit/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/include/dnsjit/input/pcap.lua b/include/dnsjit/input/pcap.lua
new file mode 100644
index 0000000..89aabd9
--- /dev/null
+++ b/include/dnsjit/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/include/dnsjit/input/zero.lua b/include/dnsjit/input/zero.lua
new file mode 100644
index 0000000..c6bf5a4
--- /dev/null
+++ b/include/dnsjit/input/zero.lua
@@ -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/>.
+
+-- dnsjit.input.zero
+-- Dummy layer to example.input.zero
+--
+-- This module has moved to example.input.zero, see examples/modules/input-example in
+-- dnsjit source repository.
+module(...,package.seeall)
+
+ok, cls = pcall(require, "example.input.zero")
+if not ok then
+ error("You need to install the example module input-example\n" .. cls)
+end
+
+return cls
diff --git a/include/dnsjit/input/zpcap.c b/include/dnsjit/input/zpcap.c
new file mode 100644
index 0000000..11eab9a
--- /dev/null
+++ b/include/dnsjit/input/zpcap.c
@@ -0,0 +1,582 @@
+/*
+ * 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/zpcap.h"
+#include "core/assert.h"
+#include "core/object/pcap.h"
+
+#include <fcntl.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>
+#include <string.h>
+
+#ifdef HAVE_LZ4
+#include <lz4frame.h>
+struct _lz4_ctx {
+ LZ4F_dctx* ctx;
+ LZ4F_decompressOptions_t opts;
+};
+#define lz4 ((struct _lz4_ctx*)self->comp_ctx)
+#endif
+#ifdef HAVE_ZSTD
+#include <zstd.h>
+struct _zstd_ctx {
+ ZSTD_DCtx* ctx;
+ ZSTD_inBuffer in;
+ ZSTD_outBuffer out;
+};
+#define zstd ((struct _zstd_ctx*)self->comp_ctx)
+#endif
+
+#define MAX_SNAPLEN 0x40000
+
+static core_log_t _log = LOG_T_INIT("input.zpcap");
+static input_zpcap_t _defaults = {
+ LOG_T_INIT_OBJ("input.zpcap"),
+ 0, 0,
+ 0, 0, 0,
+ CORE_OBJECT_PCAP_INIT(0),
+ input_zpcap_type_none, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,
+ 0
+};
+
+core_log_t* input_zpcap_log()
+{
+ return &_log;
+}
+
+void input_zpcap_init(input_zpcap_t* self)
+{
+ mlassert_self();
+
+ *self = _defaults;
+}
+
+void input_zpcap_destroy(input_zpcap_t* self)
+{
+ mlassert_self();
+
+ switch (self->compression) {
+#ifdef HAVE_LZ4
+ case input_zpcap_type_lz4: {
+ LZ4F_errorCode_t code;
+
+ if (lz4) {
+ if (lz4->ctx && (code = LZ4F_freeDecompressionContext(lz4->ctx))) {
+ lfatal("LZ4F_freeDecompressionContext() failed: %s", LZ4F_getErrorName(code));
+ }
+ }
+ free(lz4);
+ free(self->in);
+ free(self->out);
+ break;
+ }
+#endif
+#ifdef HAVE_ZSTD
+ case input_zpcap_type_zstd:
+ if (zstd) {
+ if (zstd->ctx) {
+ ZSTD_freeDCtx(zstd->ctx);
+ }
+ }
+ free(zstd);
+ free(self->in);
+ free(self->out);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ if (!self->extern_file && self->file) {
+ fclose(self->file);
+ }
+ free(self->buf);
+}
+
+static ssize_t _read(input_zpcap_t* self, void* dst, size_t len, void** dstp)
+{
+ switch (self->compression) {
+#ifdef HAVE_LZ4
+ case input_zpcap_type_lz4: {
+ size_t need = len;
+
+ if (dstp && self->out_have > need) {
+ *dstp = self->out + self->out_at;
+ self->out_have -= need;
+ self->out_at += need;
+ return len;
+ }
+
+ for (;;) {
+ if (self->out_have > need) {
+ memcpy(dst, self->out + self->out_at, need);
+ self->out_have -= need;
+ self->out_at += need;
+ return len;
+ }
+
+ memcpy(dst, self->out + self->out_at, self->out_have);
+ need -= self->out_have;
+ dst += self->out_have;
+
+ ssize_t n = fread(self->in + self->in_at, 1, self->in_size - self->in_have, self->file);
+ if (n < 0) {
+ return n;
+ }
+ self->in_at += n;
+ self->in_have += n;
+ if (!self->in_have) {
+ return 0;
+ }
+
+ size_t dst_size = self->out_size, src_size = self->in_have;
+ size_t code = LZ4F_decompress(lz4->ctx, self->out, &dst_size, self->in, &src_size, &lz4->opts);
+ if (LZ4F_isError(code)) {
+ lfatal("LZ4F_decompress() failed: %s", LZ4F_getErrorName(code));
+ }
+
+ if (src_size < self->in_have) {
+ self->in_have -= src_size;
+ memmove(self->in, self->in + src_size, self->in_have);
+ self->in_at = self->in_have;
+ } else {
+ self->in_at = 0;
+ self->in_have = 0;
+ }
+
+ self->out_at = 0;
+ self->out_have = dst_size;
+ }
+ }
+#endif
+#ifdef HAVE_ZSTD
+ case input_zpcap_type_zstd: {
+ size_t need = len;
+
+ if (dstp && self->out_have > need) {
+ *dstp = self->out + self->out_at;
+ self->out_have -= need;
+ self->out_at += need;
+ return len;
+ }
+
+ for (;;) {
+ if (self->out_have > need) {
+ memcpy(dst, self->out + self->out_at, need);
+ self->out_have -= need;
+ self->out_at += need;
+ return len;
+ }
+
+ memcpy(dst, self->out + self->out_at, self->out_have);
+ need -= self->out_have;
+ dst += self->out_have;
+
+ if (zstd->in.pos >= zstd->in.size) {
+ ssize_t n = fread(self->in, 1, self->in_size, self->file);
+ if (n < 1) {
+ return n;
+ }
+ zstd->in.size = n;
+ zstd->in.pos = 0;
+ }
+
+ zstd->out.size = self->out_size;
+ zstd->out.pos = 0;
+ size_t code = ZSTD_decompressStream(zstd->ctx, &zstd->out, &zstd->in);
+ if (ZSTD_isError(code)) {
+ lfatal("ZSTD_decompressStream() failed: %s", ZSTD_getErrorName(code));
+ }
+
+ self->out_have = zstd->out.pos;
+ self->out_at = 0;
+ }
+ }
+#endif
+ default:
+ return 0;
+ }
+}
+
+static int _open(input_zpcap_t* self)
+{
+ mlassert_self();
+
+#if _POSIX_C_SOURCE >= 200112L || defined(__FreeBSD__)
+ if (self->use_fadvise) {
+ int err = posix_fadvise(fileno((FILE*)self->file), 0, 0, POSIX_FADV_SEQUENTIAL);
+ if (err) {
+ lcritical("posix_fadvise() failed: %s", core_log_errstr(err));
+ return -2;
+ }
+ }
+#endif
+
+ switch (self->compression) {
+#ifdef HAVE_LZ4
+ case input_zpcap_type_lz4: {
+ LZ4F_errorCode_t code;
+
+ if (lz4) {
+ if (lz4->ctx && (code = LZ4F_freeDecompressionContext(lz4->ctx))) {
+ lfatal("LZ4F_freeDecompressionContext() failed: %s", LZ4F_getErrorName(code));
+ }
+ }
+ free(lz4);
+ free(self->in);
+ free(self->out);
+
+ lfatal_oom(self->comp_ctx = calloc(1, sizeof(struct _lz4_ctx)));
+ if ((code = LZ4F_createDecompressionContext(&lz4->ctx, LZ4F_VERSION))) {
+ lfatal("LZ4F_createDecompressionContext() failed: %s", LZ4F_getErrorName(code));
+ }
+ lz4->opts.stableDst = 1;
+
+ self->in_size = 256 * 1024;
+ lfatal_oom(self->in = malloc(self->in_size));
+ self->out_size = 256 * 1024;
+ lfatal_oom(self->out = malloc(self->out_size));
+
+ break;
+ }
+#endif
+#ifdef HAVE_ZSTD
+ case input_zpcap_type_zstd:
+ if (zstd) {
+ if (zstd->ctx) {
+ ZSTD_freeDCtx(zstd->ctx);
+ }
+ }
+ free(zstd);
+ free(self->in);
+ free(self->out);
+
+ lfatal_oom(self->comp_ctx = calloc(1, sizeof(struct _zstd_ctx)));
+ lfatal_oom(zstd->ctx = ZSTD_createDCtx());
+ self->in_size = ZSTD_DStreamInSize();
+ lfatal_oom(self->in = malloc(self->in_size));
+ self->out_size = ZSTD_DStreamOutSize();
+ lfatal_oom(self->out = malloc(self->out_size));
+
+ zstd->in.src = self->in;
+ zstd->out.dst = self->out;
+ zstd->out.size = self->out_size;
+ break;
+#endif
+ default:
+ lcritical("no support for selected compression");
+ return -2;
+ }
+
+ if (_read(self, &self->magic_number, 4, 0) != 4
+ || _read(self, &self->version_major, 2, 0) != 2
+ || _read(self, &self->version_minor, 2, 0) != 2
+ || _read(self, &self->thiszone, 4, 0) != 4
+ || _read(self, &self->sigfigs, 4, 0) != 4
+ || _read(self, &self->snaplen, 4, 0) != 4
+ || _read(self, &self->network, 4, 0) != 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.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_zpcap_open(input_zpcap_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_zpcap_openfp(input_zpcap_t* self, void* fp)
+{
+ mlassert_self();
+
+ if (self->file) {
+ lfatal("already opened");
+ }
+
+ self->file = fp;
+ self->extern_file = 1;
+
+ return _open(self);
+}
+
+int input_zpcap_run(input_zpcap_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.is_swapped = self->is_swapped;
+
+ while ((ret = _read(self, &hdr, 16, 0)) == 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;
+ }
+ pkt.bytes = (unsigned char*)self->buf;
+ if (_read(self, self->buf, hdr.incl_len, (void**)&pkt.bytes) != 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;
+}
+
+int input_zpcap_have_support(input_zpcap_t* self)
+{
+ mlassert_self();
+
+ switch (self->compression) {
+#ifdef HAVE_LZ4
+ case input_zpcap_type_lz4:
+ return 1;
+#endif
+#ifdef HAVE_ZSTD
+ case input_zpcap_type_zstd:
+ return 1;
+#endif
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const core_object_t* _produce(input_zpcap_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 = _read(self, &hdr, 16, 0)) != 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;
+ }
+ self->prod_pkt.bytes = (unsigned char*)self->buf;
+ if (_read(self, self->buf, hdr.incl_len, (void**)&self->prod_pkt.bytes) != 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_zpcap_producer(input_zpcap_t* self)
+{
+ mlassert_self();
+
+ if (!self->file) {
+ lfatal("no PCAP opened");
+ }
+
+ return (core_producer_t)_produce;
+}
diff --git a/include/dnsjit/input/zpcap.h b/include/dnsjit/input/zpcap.h
new file mode 100644
index 0000000..7f6f304
--- /dev/null
+++ b/include/dnsjit/input/zpcap.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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+#include <dnsjit/core/producer.h>
+#include <dnsjit/core/object/pcap.h>
+
+#ifndef __dnsjit_input_zpcap_h
+#define __dnsjit_input_zpcap_h
+
+#include <dnsjit/input/zpcap.hh>
+
+#endif
diff --git a/include/dnsjit/input/zpcap.hh b/include/dnsjit/input/zpcap.hh
new file mode 100644
index 0000000..bc013fa
--- /dev/null
+++ b/include/dnsjit/input/zpcap.hh
@@ -0,0 +1,77 @@
+/*
+ * 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 enum input_zpcap_type {
+ input_zpcap_type_none,
+ input_zpcap_type_lz4,
+ input_zpcap_type_zstd
+} input_zpcap_type_t;
+
+typedef struct input_zpcap {
+ 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;
+
+ input_zpcap_type_t compression;
+ void* comp_ctx;
+
+ void * in, *out;
+ size_t in_size, out_size;
+ size_t in_have, out_have;
+ size_t in_at, out_at;
+
+ void* file;
+ int extern_file, use_fadvise;
+ 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_zpcap_t;
+
+core_log_t* input_zpcap_log();
+
+void input_zpcap_init(input_zpcap_t* self);
+void input_zpcap_destroy(input_zpcap_t* self);
+int input_zpcap_open(input_zpcap_t* self, const char* file);
+int input_zpcap_openfp(input_zpcap_t* self, void* fp);
+int input_zpcap_run(input_zpcap_t* self);
+int input_zpcap_have_support(input_zpcap_t* self);
+
+core_producer_t input_zpcap_producer(input_zpcap_t* self);
diff --git a/include/dnsjit/input/zpcap.lua b/include/dnsjit/input/zpcap.lua
new file mode 100644
index 0000000..9a0230f
--- /dev/null
+++ b/include/dnsjit/input/zpcap.lua
@@ -0,0 +1,159 @@
+-- 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.zpcap
+-- Read input from a PCAP file that is compressed
+-- local input = require("dnsjit.input.zpcap").new()
+-- input:zstd()
+-- input:open("file.pcap.zst")
+-- input:receiver(filter_or_output)
+-- input:run()
+--
+-- Read input from a PCAP file that is compressed 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.zpcap_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "input_zpcap_t"
+local input_zpcap_t = ffi.typeof(t_name)
+local Zpcap = {}
+
+-- Create a new Zpcap input.
+function Zpcap.new()
+ local self = {
+ _receiver = nil,
+ obj = input_zpcap_t(),
+ }
+ C.input_zpcap_init(self.obj)
+ ffi.gc(self.obj, C.input_zpcap_destroy)
+ return setmetatable(self, { __index = Zpcap })
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Zpcap:log()
+ if self == nil then
+ return C.input_zpcap_log()
+ end
+ return self.obj._log
+end
+
+-- Set the receiver to pass objects to.
+function Zpcap:receiver(o)
+ self.obj.recv, self.obj.ctx = o:receive()
+ self._receiver = o
+end
+
+-- Return the C functions and context for producing objects.
+function Zpcap:produce()
+ return C.input_zpcap_producer(self.obj), self.obj
+end
+
+-- Use
+-- .B posix_fadvise()
+-- to indicate sequential reading (if supported), may increase performance.
+-- MUST be called before
+-- .BR open() .
+function Zpcap:fadvise_sequential()
+ self.obj.use_fadvise = 1
+end
+
+-- Use liblz4 to decompress the input file/data.
+function Zpcap:lz4()
+ self.obj.compression = "input_zpcap_type_lz4"
+end
+
+-- Use libzstd to decompress the input file/data.
+function Zpcap:zstd()
+ self.obj.compression = "input_zpcap_type_zstd"
+end
+
+-- Return true if support for selected compression library is built in.
+function Zpcap:have_support()
+ if C.input_zpcap_have_support(self.obj) == 1 then
+ return true
+ end
+ return false
+end
+
+-- Open a PCAP file for processing and read the PCAP header.
+-- Returns 0 on success.
+function Zpcap:open(file)
+ return C.input_zpcap_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 Zpcap:openfp(fp)
+ return C.input_zpcap_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 Zpcap:run()
+ return C.input_zpcap_run(self.obj)
+end
+
+-- Return the number of packets seen.
+function Zpcap:packets()
+ return tonumber(self.obj.pkts)
+end
+
+-- dnsjit.input.fpcap (3)
+return Zpcap
diff --git a/include/dnsjit/lib.lua b/include/dnsjit/lib.lua
new file mode 100644
index 0000000..d249ea8
--- /dev/null
+++ b/include/dnsjit/lib.lua
@@ -0,0 +1,29 @@
+-- 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.lib
+-- Various Lua libraries or C library bindings
+module(...,package.seeall)
+
+-- dnsjit.lib.base64url (3),
+-- dnsjit.lib.clock (3),
+-- dnsjit.lib.getopt (3),
+-- dnsjit.lib.ip (3),
+-- dnsjit.lib.parseconf (3),
+-- dnsjit.lib.trie (3)
+return
diff --git a/include/dnsjit/lib/base64url.c b/include/dnsjit/lib/base64url.c
new file mode 100644
index 0000000..aacd490
--- /dev/null
+++ b/include/dnsjit/lib/base64url.c
@@ -0,0 +1,480 @@
+/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program 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.
+
+ This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "lib/base64url.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+/*! \brief Maximal length of binary input to Base64url encoding. */
+#define MAX_BIN_DATA_LEN ((INT32_MAX / 4) * 3)
+
+/*! \brief Base64url padding character. */
+static const uint8_t base64url_pad = '\0';
+/*! \brief Base64 alphabet. */
+static const uint8_t base64url_enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+/*! \brief Indicates bad Base64 character. */
+#define KO 255
+/*! \brief Indicates Base64 padding character. */
+#define PD 64
+
+/*! \brief Transformation and validation table for decoding Base64. */
+static const uint8_t base64url_dec[256] = {
+ [0] = PD,
+ [43] = KO,
+ ['V'] = 21,
+ [129] = KO,
+ [172] = KO,
+ [215] = KO,
+ [1] = KO,
+ [44] = KO,
+ ['W'] = 22,
+ [130] = KO,
+ [173] = KO,
+ [216] = KO,
+ [2] = KO,
+ ['-'] = 62,
+ ['X'] = 23,
+ [131] = KO,
+ [174] = KO,
+ [217] = KO,
+ [3] = KO,
+ [46] = KO,
+ ['Y'] = 24,
+ [132] = KO,
+ [175] = KO,
+ [218] = KO,
+ [4] = KO,
+ [47] = KO,
+ ['Z'] = 25,
+ [133] = KO,
+ [176] = KO,
+ [219] = KO,
+ [5] = KO,
+ ['0'] = 52,
+ [91] = KO,
+ [134] = KO,
+ [177] = KO,
+ [220] = KO,
+ [6] = KO,
+ ['1'] = 53,
+ [92] = KO,
+ [135] = KO,
+ [178] = KO,
+ [221] = KO,
+ [7] = KO,
+ ['2'] = 54,
+ [93] = KO,
+ [136] = KO,
+ [179] = KO,
+ [222] = KO,
+ [8] = KO,
+ ['3'] = 55,
+ [94] = KO,
+ [137] = KO,
+ [180] = KO,
+ [223] = KO,
+ [9] = KO,
+ ['4'] = 56,
+ ['_'] = 63,
+ [138] = KO,
+ [181] = KO,
+ [224] = KO,
+ [10] = KO,
+ ['5'] = 57,
+ [96] = KO,
+ [139] = KO,
+ [182] = KO,
+ [225] = KO,
+ [11] = KO,
+ ['6'] = 58,
+ ['a'] = 26,
+ [140] = KO,
+ [183] = KO,
+ [226] = KO,
+ [12] = KO,
+ ['7'] = 59,
+ ['b'] = 27,
+ [141] = KO,
+ [184] = KO,
+ [227] = KO,
+ [13] = KO,
+ ['8'] = 60,
+ ['c'] = 28,
+ [142] = KO,
+ [185] = KO,
+ [228] = KO,
+ [14] = KO,
+ ['9'] = 61,
+ ['d'] = 29,
+ [143] = KO,
+ [186] = KO,
+ [229] = KO,
+ [15] = KO,
+ [58] = KO,
+ ['e'] = 30,
+ [144] = KO,
+ [187] = KO,
+ [230] = KO,
+ [16] = KO,
+ [59] = KO,
+ ['f'] = 31,
+ [145] = KO,
+ [188] = KO,
+ [231] = KO,
+ [17] = KO,
+ [60] = KO,
+ ['g'] = 32,
+ [146] = KO,
+ [189] = KO,
+ [232] = KO,
+ [18] = KO,
+ [61] = KO,
+ ['h'] = 33,
+ [147] = KO,
+ [190] = KO,
+ [233] = KO,
+ [19] = KO,
+ [62] = KO,
+ ['i'] = 34,
+ [148] = KO,
+ [191] = KO,
+ [234] = KO,
+ [20] = KO,
+ [63] = KO,
+ ['j'] = 35,
+ [149] = KO,
+ [192] = KO,
+ [235] = KO,
+ [21] = KO,
+ [64] = KO,
+ ['k'] = 36,
+ [150] = KO,
+ [193] = KO,
+ [236] = KO,
+ [22] = KO,
+ ['A'] = 0,
+ ['l'] = 37,
+ [151] = KO,
+ [194] = KO,
+ [237] = KO,
+ [23] = KO,
+ ['B'] = 1,
+ ['m'] = 38,
+ [152] = KO,
+ [195] = KO,
+ [238] = KO,
+ [24] = KO,
+ ['C'] = 2,
+ ['n'] = 39,
+ [153] = KO,
+ [196] = KO,
+ [239] = KO,
+ [25] = KO,
+ ['D'] = 3,
+ ['o'] = 40,
+ [154] = KO,
+ [197] = KO,
+ [240] = KO,
+ [26] = KO,
+ ['E'] = 4,
+ ['p'] = 41,
+ [155] = KO,
+ [198] = KO,
+ [241] = KO,
+ [27] = KO,
+ ['F'] = 5,
+ ['q'] = 42,
+ [156] = KO,
+ [199] = KO,
+ [242] = KO,
+ [28] = KO,
+ ['G'] = 6,
+ ['r'] = 43,
+ [157] = KO,
+ [200] = KO,
+ [243] = KO,
+ [29] = KO,
+ ['H'] = 7,
+ ['s'] = 44,
+ [158] = KO,
+ [201] = KO,
+ [244] = KO,
+ [30] = KO,
+ ['I'] = 8,
+ ['t'] = 45,
+ [159] = KO,
+ [202] = KO,
+ [245] = KO,
+ [31] = KO,
+ ['J'] = 9,
+ ['u'] = 46,
+ [160] = KO,
+ [203] = KO,
+ [246] = KO,
+ [32] = KO,
+ ['K'] = 10,
+ ['v'] = 47,
+ [161] = KO,
+ [204] = KO,
+ [247] = KO,
+ [33] = KO,
+ ['L'] = 11,
+ ['w'] = 48,
+ [162] = KO,
+ [205] = KO,
+ [248] = KO,
+ [34] = KO,
+ ['M'] = 12,
+ ['x'] = 49,
+ [163] = KO,
+ [206] = KO,
+ [249] = KO,
+ [35] = KO,
+ ['N'] = 13,
+ ['y'] = 50,
+ [164] = KO,
+ [207] = KO,
+ [250] = KO,
+ [36] = KO,
+ ['O'] = 14,
+ ['z'] = 51,
+ [165] = KO,
+ [208] = KO,
+ [251] = KO,
+ [37] = KO,
+ ['P'] = 15,
+ [123] = KO,
+ [166] = KO,
+ [209] = KO,
+ [252] = KO,
+ [38] = KO,
+ ['Q'] = 16,
+ [124] = KO,
+ [167] = KO,
+ [210] = KO,
+ [253] = KO,
+ [39] = KO,
+ ['R'] = 17,
+ [125] = KO,
+ [168] = KO,
+ [211] = KO,
+ [254] = KO,
+ [40] = KO,
+ ['S'] = 18,
+ [126] = KO,
+ [169] = KO,
+ [212] = KO,
+ [255] = KO,
+ [41] = KO,
+ ['T'] = 19,
+ [127] = KO,
+ [170] = KO,
+ [213] = KO,
+ [42] = KO,
+ ['U'] = 20,
+ [128] = KO,
+ [171] = KO,
+ [214] = KO,
+};
+
+int32_t base64url_encode(const uint8_t* in,
+ const uint32_t in_len,
+ uint8_t* out,
+ const uint32_t out_len)
+{
+ // Checking inputs.
+ if (in == NULL || out == NULL) {
+ return -EINVAL;
+ }
+ if (in_len > MAX_BIN_DATA_LEN || out_len < ((in_len + 2) / 3) * 4) {
+ return -ERANGE;
+ }
+
+ uint8_t rest_len = in_len % 3;
+ const uint8_t* stop = in + in_len - rest_len;
+ uint8_t* text = out;
+
+ // Encoding loop takes 3 bytes and creates 4 characters.
+ while (in < stop) {
+ text[0] = base64url_enc[in[0] >> 2];
+ text[1] = base64url_enc[(in[0] & 0x03) << 4 | in[1] >> 4];
+ text[2] = base64url_enc[(in[1] & 0x0F) << 2 | in[2] >> 6];
+ text[3] = base64url_enc[in[2] & 0x3F];
+ text += 4;
+ in += 3;
+ }
+
+ // Processing of padding, if any.
+ switch (rest_len) {
+ case 2:
+ text[0] = base64url_enc[in[0] >> 2];
+ text[1] = base64url_enc[(in[0] & 0x03) << 4 | in[1] >> 4];
+ text[2] = base64url_enc[(in[1] & 0x0F) << 2];
+ text[3] = base64url_pad;
+ text += 3;
+ break;
+ case 1:
+ text[0] = base64url_enc[in[0] >> 2];
+ text[1] = base64url_enc[(in[0] & 0x03) << 4];
+ text[2] = base64url_pad;
+ text[3] = base64url_pad;
+ text += 2;
+ break;
+ }
+ return (text - out);
+}
+
+int32_t base64url_encode_alloc(const uint8_t* in,
+ const uint32_t in_len,
+ uint8_t** out)
+{
+ // Checking inputs.
+ if (out == NULL) {
+ return -EINVAL;
+ }
+ if (in_len > MAX_BIN_DATA_LEN) {
+ return -ERANGE;
+ }
+
+ // Compute output buffer length.
+ uint32_t out_len = ((in_len + 2) / 3) * 4;
+
+ // Allocate output buffer.
+ *out = malloc(out_len);
+ if (*out == NULL) {
+ return -ENOMEM;
+ }
+
+ // Encode data.
+ int32_t ret = base64url_encode(in, in_len, *out, out_len);
+ if (ret < 0) {
+ free(*out);
+ *out = NULL;
+ }
+
+ return ret;
+}
+
+int32_t base64url_decode(const uint8_t* in,
+ const uint32_t in_len,
+ uint8_t* out,
+ const uint32_t out_len)
+{
+ // Checking inputs.
+ if (in == NULL || out == NULL) {
+ return -EINVAL;
+ }
+ if (in_len > INT32_MAX || out_len < ((in_len + 3) / 4) * 3) {
+ return -ERANGE;
+ }
+
+ const uint8_t* stop = in + in_len;
+ uint8_t* bin = out;
+ uint8_t pad_len = 0;
+ uint8_t c1, c2, c3, c4;
+
+ // Decoding loop takes 4 characters and creates 3 bytes.
+ while (in < stop) {
+ // Filling and transforming 4 Base64 chars.
+ c1 = base64url_dec[in[0]];
+ c2 = (in + 1 < stop) ? base64url_dec[in[1]] : PD;
+ c3 = (in + 2 < stop) ? base64url_dec[in[2]] : PD;
+ c4 = (in + 3 < stop) ? base64url_dec[in[3]] : PD;
+
+ // Check 4. char if is bad or padding.
+ if (c4 >= PD) {
+ if (c4 == PD && pad_len == 0) {
+ pad_len = 1;
+ } else {
+ return -1;
+ }
+ }
+
+ // Check 3. char if is bad or padding.
+ if (c3 >= PD) {
+ if (c3 == PD && pad_len == 1) {
+ pad_len = 2;
+ } else {
+ return -1;
+ }
+ }
+
+ // Check 1. and 2. chars if are not padding.
+ if (c2 >= PD || c1 >= PD) {
+ return -1;
+ }
+
+ // Computing of output data based on padding length.
+ switch (pad_len) {
+ case 0:
+ bin[2] = (c3 << 6) + c4;
+ // FALLTHROUGH
+ case 1:
+ bin[1] = (c2 << 4) + (c3 >> 2);
+ // FALLTHROUGH
+ case 2:
+ bin[0] = (c1 << 2) + (c2 >> 4);
+ }
+
+ // Update output end.
+ switch (pad_len) {
+ case 0:
+ bin += 3;
+ break;
+ case 1:
+ bin += 2;
+ break;
+ case 2:
+ bin += 1;
+ break;
+ }
+
+ in += 4;
+ }
+
+ return (bin - out);
+}
+
+int32_t base64url_decode_alloc(const uint8_t* in,
+ const uint32_t in_len,
+ uint8_t** out)
+{
+ // Checking inputs.
+ if (out == NULL) {
+ return -EINVAL;
+ }
+
+ // Compute output buffer length.
+ uint32_t out_len = ((in_len + 3) / 4) * 3;
+
+ // Allocate output buffer.
+ *out = malloc(out_len);
+ if (*out == NULL) {
+ return -ENOMEM;
+ }
+
+ // Decode data.
+ int32_t ret = base64url_decode(in, in_len, *out, out_len);
+ if (ret < 0) {
+ free(*out);
+ *out = NULL;
+ }
+
+ return ret;
+}
diff --git a/include/dnsjit/lib/base64url.h b/include/dnsjit/lib/base64url.h
new file mode 100644
index 0000000..f2f5808
--- /dev/null
+++ b/include/dnsjit/lib/base64url.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2020, CZ.NIC, z.s.p.o.
+ * 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 <stdint.h>
+
+#ifndef __dnsjit_lib_base64url_h
+#define __dnsjit_lib_base64url_h
+
+#include <dnsjit/lib/base64url.hh>
+
+#endif
diff --git a/include/dnsjit/lib/base64url.hh b/include/dnsjit/lib/base64url.hh
new file mode 100644
index 0000000..5f19a10
--- /dev/null
+++ b/include/dnsjit/lib/base64url.hh
@@ -0,0 +1,99 @@
+/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+ This program 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.
+
+ This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \brief Base64url implementation (RFC 4648).
+ */
+
+/*!
+ * \brief Encodes binary data using Base64.
+ *
+ * \note Output data buffer contains Base64 text string which isn't
+ * terminated with '\0'!
+ *
+ * \param in Input binary data.
+ * \param in_len Length of input data.
+ * \param out Output data buffer.
+ * \param out_len Size of output buffer.
+ *
+ * \retval >=0 length of output string.
+ * \retval KNOT_E* if error.
+ */
+int32_t base64url_encode(const uint8_t* in,
+ const uint32_t in_len,
+ uint8_t* out,
+ const uint32_t out_len);
+
+/*!
+ * \brief Encodes binary data using Base64 and output stores to own buffer.
+ *
+ * \note Output data buffer contains Base64 text string which isn't
+ * terminated with '\0'!
+ *
+ * \note Output buffer should be deallocated after use.
+ *
+ * \param in Input binary data.
+ * \param in_len Length of input data.
+ * \param out Output data buffer.
+ *
+ * \retval >=0 length of output string.
+ * \retval KNOT_E* if error.
+ */
+int32_t base64url_encode_alloc(const uint8_t* in,
+ const uint32_t in_len,
+ uint8_t** out);
+
+/*!
+ * \brief Decodes text data using Base64.
+ *
+ * \note Input data needn't be terminated with '\0'.
+ *
+ * \note Input data must be continuous Base64 string!
+ *
+ * \param in Input text data.
+ * \param in_len Length of input string.
+ * \param out Output data buffer.
+ * \param out_len Size of output buffer.
+ *
+ * \retval >=0 length of output data.
+ * \retval KNOT_E* if error.
+ */
+int32_t base64url_decode(const uint8_t* in,
+ const uint32_t in_len,
+ uint8_t* out,
+ const uint32_t out_len);
+
+/*!
+ * \brief Decodes text data using Base64 and output stores to own buffer.
+ *
+ * \note Input data needn't be terminated with '\0'.
+ *
+ * \note Input data must be continuous Base64 string!
+ *
+ * \note Output buffer should be deallocated after use.
+ *
+ * \param in Input text data.
+ * \param in_len Length of input string.
+ * \param out Output data buffer.
+ *
+ * \retval >=0 length of output data.
+ * \retval KNOT_E* if error.
+ */
+int32_t base64url_decode_alloc(const uint8_t* in,
+ const uint32_t in_len,
+ uint8_t** out);
+
+/*! @} */
diff --git a/include/dnsjit/lib/base64url.lua b/include/dnsjit/lib/base64url.lua
new file mode 100644
index 0000000..8a408f5
--- /dev/null
+++ b/include/dnsjit/lib/base64url.lua
@@ -0,0 +1,97 @@
+-- Copyright (c) 2020, CZ.NIC, z.s.p.o.
+-- 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.lib.base64url
+-- Utility library to convert data to base64url format
+-- local base64url = require("dnsjit.lib.base64url")
+-- .SS Encoding and decoding lua strings
+-- local encoded = base64url.encode("abcd")
+-- local decoded = base64url.decode(encoded)
+-- .SS Encoding C byte arrays
+-- local pl -- pl is core.object.payload
+-- local encoded = base64url.encode(pl.payload, pl.len)
+--
+-- Encode and decode data to/from base64url format.
+module(...,package.seeall)
+
+require("dnsjit.lib.base64url_h")
+local ffi = require("ffi")
+local C = ffi.C
+local log = require("dnsjit.core.log")
+local module_log = log.new("lib.base64url")
+
+Base64Url = {}
+
+-- Encode lua string or C byte array to base64url representation.
+-- The input string may contain non-printable characters.
+--
+-- .B data_len
+-- is length of the input data (optional for lua strings, required for
+-- C byte arrays).
+function Base64Url.encode(data, data_len)
+ data_len = tonumber(data_len) -- in case of cdata length
+ if type(data) == "cdata" then
+ if type(data_len) ~= "number" then
+ module_log:fatal("encode: data_len must be specified for cdata")
+ return
+ end
+ elseif type(data) ~= "string" then
+ module_log:fatal("encode: input must be string")
+ return
+ end
+
+ if data_len ~= nil and data_len < 0 then
+ module_log:fatal("encode: data_len must be greater than 0")
+ return
+ end
+
+ local in_len = data_len or string.len(data)
+ local buf_len = math.ceil(4 * in_len / 3) + 2
+ local buf = ffi.new("uint8_t[?]", buf_len)
+ local out_len = ffi.C.base64url_encode(data, in_len, buf, buf_len)
+ if out_len < 0 then
+ module_log:critical("encode: error ("..log.errstr(-out_len)..")")
+ return
+ end
+ return ffi.string(buf, out_len)
+end
+
+-- Decode a base64url encoded lua string.
+-- The output string may contain non-printable characters.
+function Base64Url.decode(data)
+ if type(data) ~= "string" then
+ module_log:fatal("decode: input must be string")
+ return
+ end
+
+ local in_len = string.len(data)
+ local buf_len = math.ceil(3 * in_len / 4) + 1
+ local buf = ffi.new("uint8_t[?]", buf_len)
+ local out_len = ffi.C.base64url_decode(data, in_len, buf, buf_len)
+ if out_len == -34 then -- ERANGE
+ module_log:critical("decode: error "..log.errstr(-out_len).." - invalid character(s) in input string?")
+ return
+ elseif out_len < 0 then
+ module_log:critical("decode: error "..log.errstr(-out_len))
+ return
+ end
+ return ffi.string(buf, out_len)
+end
+
+-- dnsjit.core.object.payload (3)
+return Base64Url
diff --git a/include/dnsjit/lib/clock.c b/include/dnsjit/lib/clock.c
new file mode 100644
index 0000000..0676648
--- /dev/null
+++ b/include/dnsjit/lib/clock.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 "lib/clock.h"
+
+#include <time.h>
+
+core_timespec_t lib_clock_getres(lib_clock_clkid_t clkid)
+{
+ struct timespec ts;
+ core_timespec_t ret = { 0, 0 };
+ clockid_t clk_id;
+
+ switch (clkid) {
+ case LIB_CLOCK_REALTIME:
+ clk_id = CLOCK_REALTIME;
+ break;
+ case LIB_CLOCK_MONOTONIC:
+ clk_id = CLOCK_MONOTONIC;
+ break;
+ default:
+ return ret;
+ }
+
+ if (!clock_getres(clk_id, &ts)) {
+ ret.sec = ts.tv_sec;
+ ret.nsec = ts.tv_nsec;
+ }
+
+ return ret;
+}
+
+core_timespec_t lib_clock_gettime(lib_clock_clkid_t clkid)
+{
+ struct timespec ts;
+ core_timespec_t ret = { 0, 0 };
+ clockid_t clk_id;
+
+ switch (clkid) {
+ case LIB_CLOCK_REALTIME:
+ clk_id = CLOCK_REALTIME;
+ break;
+ case LIB_CLOCK_MONOTONIC:
+ clk_id = CLOCK_MONOTONIC;
+ break;
+ default:
+ return ret;
+ }
+
+ if (!clock_gettime(clk_id, &ts)) {
+ ret.sec = ts.tv_sec;
+ ret.nsec = ts.tv_nsec;
+ }
+
+ return ret;
+}
diff --git a/include/dnsjit/lib/clock.h b/include/dnsjit/lib/clock.h
new file mode 100644
index 0000000..48d16ac
--- /dev/null
+++ b/include/dnsjit/lib/clock.h
@@ -0,0 +1,28 @@
+/*
+ * 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 <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_lib_clock_h
+#define __dnsjit_lib_clock_h
+
+#include <dnsjit/lib/clock.hh>
+
+#endif
diff --git a/include/dnsjit/lib/clock.hh b/include/dnsjit/lib/clock.hh
new file mode 100644
index 0000000..9463815
--- /dev/null
+++ b/include/dnsjit/lib/clock.hh
@@ -0,0 +1,29 @@
+/*
+ * 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.timespec_h")
+
+typedef enum lib_clock_clkid {
+ LIB_CLOCK_REALTIME,
+ LIB_CLOCK_MONOTONIC
+} lib_clock_clkid_t;
+
+core_timespec_t lib_clock_getres(lib_clock_clkid_t clkid);
+core_timespec_t lib_clock_gettime(lib_clock_clkid_t clkid);
diff --git a/include/dnsjit/lib/clock.lua b/include/dnsjit/lib/clock.lua
new file mode 100644
index 0000000..60ee9f6
--- /dev/null
+++ b/include/dnsjit/lib/clock.lua
@@ -0,0 +1,47 @@
+-- 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.lib.clock
+-- Clock and time functions
+-- local clock = require("dnsjit.lib.clock")
+-- local sec, nsec = clock.monotonic()
+--
+-- Functions to get the time from system-wide clocks.
+module(...,package.seeall)
+
+require("dnsjit.lib.clock_h")
+local C = require("ffi").C
+
+Clock = {}
+
+-- Return the current seconds and nanoseconds (as a list) from the realtime
+-- clock.
+function Clock.realtime()
+ local ts = C.lib_clock_gettime("LIB_CLOCK_REALTIME")
+ return tonumber(ts.sec), tonumber(ts.nsec)
+end
+
+-- Return the current seconds and nanoseconds (as a list) from the monotonic
+-- clock.
+function Clock.monotonic()
+ local ts = C.lib_clock_gettime("LIB_CLOCK_MONOTONIC")
+ return tonumber(ts.sec), tonumber(ts.nsec)
+end
+
+-- clock_gettime (2)
+return Clock
diff --git a/include/dnsjit/lib/getopt.lua b/include/dnsjit/lib/getopt.lua
new file mode 100644
index 0000000..ede3e12
--- /dev/null
+++ b/include/dnsjit/lib/getopt.lua
@@ -0,0 +1,368 @@
+-- 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.lib.getopt
+-- Parse and handle arguments
+-- local getopt = require("dnsjit.lib.getopt").new({
+-- { "v", "verbose", 0, "Enable verbosity", "?+" },
+-- { nil, "host", "localhost", "Set host", "?" },
+-- { "p", nil, 53, "Set port", "?" },
+-- })
+-- .
+-- local left = getopt:parse()
+-- .
+-- print("host", getopt:val("host"))
+-- print("port", getopt:val("p"))
+--
+-- A "getopt long" implementation to easily handle command line arguments
+-- and display usage.
+-- An option is the short name (one character), long name,
+-- default value (which also defines the type), help text and extensions.
+-- Options are by default required, see extensions to change this.
+-- .LP
+-- The Lua types allowed are
+-- .BR boolean ,
+-- .BR string ,
+-- .BR number .
+-- .LP
+-- The extensions available are:
+-- .TP
+-- .B ?
+-- Make the option optional.
+-- .TP
+-- .B *
+-- For string and number options this make it possible to specified it
+-- multiple times and all values will be returned in a table.
+-- .TP
+-- .B +
+-- For number options this will act as an counter increaser, the value will
+-- be the default value + 1 for each time the option is given.
+-- .LP
+-- Option
+-- .I -h
+-- and
+-- .I --help
+-- are automatically added if the option
+-- .I --help
+-- is not already defined.
+-- .SS Attributes
+-- .TP
+-- left
+-- A table that contains the arguments left after parsing, same as returned by
+-- .IR parse() .
+-- .TP
+-- usage_desc
+-- A string that describes the usage of the program, if not set then the
+-- default will be "
+-- .I "program [options...]"
+-- ".
+module(...,package.seeall)
+
+local log = require("dnsjit.core.log")
+
+local module_log = log.new("lib.getopt")
+Getopt = {}
+
+-- Create a new Getopt object.
+-- .I args
+-- is a table with tables that specifies the options available.
+-- Each entry is unpacked and sent to
+-- .BR Getopt:add() .
+function Getopt.new(args)
+ local self = setmetatable({
+ left = {},
+ usage_desc = nil,
+ _opt = {},
+ _s2l = {},
+ _log = log.new("lib.getopt", module_log),
+ }, { __index = Getopt })
+
+ self._log:debug("new()")
+
+ for k, v in pairs(args) do
+ local short, long, default, help, extensions = unpack(v)
+ self:add(short, long, default, help, extensions)
+ end
+
+ return self
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Getopt:log()
+ if self == nil then
+ return module_log
+ end
+ return self._log
+end
+
+-- Add an option.
+function Getopt:add(short, long, default, help, extensions)
+ local optional = false
+ local multiple = false
+ local counter = false
+ local name = long or short
+
+ if type(name) ~= "string" then
+ error("long|short) need to be a string")
+ elseif name == "" then
+ error("name (long|short) needs to be set")
+ end
+ if short and (type(short) ~= "string" or #short ~= 1) then
+ error("short needs to be a string of length 1")
+ end
+
+ if self._opt[name] then
+ error("option "..name.." alredy exists")
+ elseif short and self._s2l[short] then
+ error("option "..short.." alredy exists")
+ end
+
+ local t = type(default)
+ if t ~= "string" and t ~= "number" and t ~= "boolean" then
+ error("option "..name..": invalid type "..t)
+ end
+
+ if type(extensions) == "string" then
+ local n
+ for n = 1, extensions:len() do
+ local extension = extensions:sub(n, n)
+ if extension == "?" then
+ optional = true
+ elseif extension == "*" then
+ multiple = true
+ elseif extension == "+" then
+ counter = true
+ else
+ error("option "..name..": invalid extension "..extension)
+ end
+ end
+ end
+
+ self._opt[name] = {
+ value = nil,
+ short = short,
+ long = long,
+ type = t,
+ default = default,
+ help = help,
+ optional = optional,
+ multiple = multiple,
+ counter = counter,
+ }
+ if long and short then
+ self._s2l[short] = long
+ elseif short and not long then
+ self._s2l[short] = short
+ end
+
+ if not self._opt["help"] then
+ self._opt["help"] = {
+ short = nil,
+ long = "help",
+ type = "boolean",
+ default = false,
+ help = "Display this help text",
+ optional = true,
+ }
+ if not self._s2l["h"] then
+ self._opt["help"].short = "h"
+ self._s2l["h"] = "help"
+ end
+ end
+end
+
+-- Print the usage.
+function Getopt:usage()
+ if self.usage_desc then
+ print("usage: " .. self.usage_desc)
+ else
+ print("usage: program [options...]")
+ end
+
+ local opts = {}
+ for k, _ in pairs(self._opt) do
+ if k ~= "help" then
+ table.insert(opts, k)
+ end
+ end
+ table.sort(opts)
+ table.insert(opts, "help")
+
+ for _, k in pairs(opts) do
+ local v = self._opt[k]
+ local arg
+ if v.type == "string" then
+ arg = " \""..v.default.."\""
+ elseif v.type == "number" and v.counter == false then
+ arg = " "..v.default
+ else
+ arg = ""
+ end
+ if v.long then
+ print("", (v.short and "-"..v.short or " ").." --"..v.long..arg, v.help)
+ else
+ print("", "-"..v.short..arg, v.help)
+ end
+ end
+end
+
+-- Parse the options.
+-- If
+-- .I args
+-- is not specified or nil then the global
+-- .B arg
+-- is used.
+-- If
+-- .I startn
+-- is given, it will start parsing arguments in the table from that position.
+-- The default position to start at is 2 for
+-- .IR dnsjit ,
+-- see
+-- .BR dnsjit.core (3).
+function Getopt:parse(args, startn)
+ if not args then
+ args = arg
+ end
+
+ local n
+ local opt = nil
+ local left = {}
+ local need_arg = false
+ local stop = false
+ local name
+ for n = startn or 2, table.maxn(args) do
+ if need_arg then
+ if opt.multiple then
+ if opt.value == nil then
+ opt.value = {}
+ end
+ if opt.type == "number" then
+ table.insert(opt.value, tonumber(args[n]))
+ else
+ table.insert(opt.value, args[n])
+ end
+ else
+ if opt.type == "number" then
+ opt.value = tonumber(args[n])
+ else
+ opt.value = args[n]
+ end
+ end
+ need_arg = false
+ elseif stop or args[n] == "-" then
+ table.insert(left, args[n])
+ elseif args[n] == "--" then
+ stop = true
+ elseif args[n]:sub(1, 1) == "-" then
+ if args[n]:sub(1, 2) == "--" then
+ name = args[n]:sub(3)
+ else
+ name = args[n]:sub(2)
+ if name:len() > 1 then
+ local n2, name2
+ for n2 = 1, name:len() - 1 do
+ name2 = name:sub(n2, n2)
+ opt = self._opt[self._s2l[name2]]
+ if not opt then
+ error("unknown option "..name2)
+ end
+ if opt.type == "number" and opt.counter then
+ if opt.value == nil then
+ opt.value = opt.default
+ end
+ opt.value = opt.value + 1
+ elseif opt.type == "boolean" then
+ if opt.value == nil then
+ opt.value = opt.default
+ end
+ if opt.value then
+ opt.value = false
+ else
+ opt.value = true
+ end
+ else
+ error("invalid short option '"..name2.."' in multioption statement")
+ end
+ end
+ name = name:sub(-1)
+ end
+ end
+ if self._s2l[name] then
+ name = self._s2l[name]
+ end
+ if not self._opt[name] then
+ error("unknown option "..name)
+ end
+ opt = self._opt[name]
+ if opt.type == "string" then
+ need_arg = true
+ elseif opt.type == "number" then
+ if opt.counter then
+ if opt.value == nil then
+ opt.value = opt.default
+ end
+ opt.value = opt.value + 1
+ else
+ need_arg = true
+ end
+ elseif opt.type == "boolean" then
+ if opt.value == nil then
+ opt.value = opt.default
+ end
+ if opt.value then
+ opt.value = false
+ else
+ opt.value = true
+ end
+ else
+ error("internal error, invalid option type "..opt.type)
+ end
+ else
+ table.insert(left, args[n])
+ end
+ end
+
+ if need_arg then
+ error("option "..name.." needs argument")
+ end
+
+ for k, v in pairs(self._opt) do
+ if v.optional == false and v.value == nil then
+ error("missing required option "..k.."")
+ end
+ end
+
+ self.left = left
+ return left
+end
+
+-- Return the value of an option.
+function Getopt:val(name)
+ local opt = self._opt[name] or self._opt[self._s2l[name]]
+ if not opt then
+ return
+ end
+ if opt.value == nil then
+ return opt.default
+ else
+ return opt.value
+ end
+end
+
+-- dnsjit.core (3)
+return Getopt
diff --git a/include/dnsjit/lib/ip.lua b/include/dnsjit/lib/ip.lua
new file mode 100644
index 0000000..74dc85a
--- /dev/null
+++ b/include/dnsjit/lib/ip.lua
@@ -0,0 +1,125 @@
+-- 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.lib.ip
+-- IP address utility library
+-- local ip = require("dnsjit.lib.ip")
+-- print(ip.ipstring(ipv4_cdata))
+-- print(ip.ip6string(ipv6_cdata), true)
+--
+-- A library to help with various IP address related tasks, such as
+-- printing them.
+module(...,package.seeall)
+
+local ffi = require("ffi")
+
+Ip = {}
+
+-- Return an IPv4 or IPv6 address as a string.
+-- If it's an IPv6 address the optional argument
+-- .I pretty
+-- is true then return an easier to read IPv6 address.
+-- Return an empty string on invalid input.
+function Ip.tostring(ip, pretty)
+ if type(ip) == "cdata" then
+ if ffi.sizeof(ip) == 4 then
+ return Ip.ipstring(ip)
+ elseif ffi.sizeof(ip) == 16 then
+ return Ip.ip6string(ip, pretty)
+ end
+ end
+ return ""
+end
+
+-- Return a IPv4 address as a string.
+-- The input is a 4-byte cdata array.
+function Ip.ipstring(ip)
+ return ip[0] ..".".. ip[1] ..".".. ip[2] ..".".. ip[3]
+end
+
+local function _pretty(ip)
+ local src = {}
+
+ local n, nn
+ nn = 1
+ for n = 0, 15, 2 do
+ if ip[n] ~= 0 then
+ src[nn] = string.format("%x%02x", ip[n], ip[n + 1])
+ elseif ip[n + 1] ~= 0 then
+ src[nn] = string.format("%x", ip[n + 1])
+ else
+ src[nn] = "0"
+ end
+ nn = nn + 1
+ end
+
+ local best_n, best_at, at = 0, 0, 0
+ n = 0
+ for nn = 1, 8 do
+ if src[nn] == "0" then
+ if n == 0 then
+ at = nn
+ end
+ n = n + 1
+ else
+ if n > 0 then
+ if n > best_n then
+ best_n = n
+ best_at = at
+ end
+ n = 0
+ end
+ end
+ end
+ if n > 0 then
+ if n > best_n then
+ best_n = n
+ best_at = at
+ end
+ end
+ if best_n > 1 then
+ for n = 2, best_n do
+ table.remove(src, best_at)
+ end
+ if best_at == 1 or best_at + best_n > 8 then
+ src[best_at] = ":"
+ else
+ src[best_at] = ""
+ end
+ end
+
+ return table.concat(src,":")
+end
+
+-- Return the IPv6 address as a string.
+-- The input is a 16-byte cdata array.
+-- If
+-- .I pretty
+-- is true then return an easier to read IPv6 address.
+function Ip.ip6string(ip6, pretty)
+ if pretty == true then
+ return _pretty(ip6)
+ end
+ return string.format("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+ ip6[0], ip6[1], ip6[2], ip6[3], ip6[4], ip6[5], ip6[6], ip6[7],
+ ip6[8], ip6[9], ip6[10], ip6[11], ip6[12], ip6[13], ip6[14], ip6[15])
+end
+
+-- dnsjit.core.object.ip (3),
+-- dnsjit.core.object.ip6 (3)
+return Ip
diff --git a/include/dnsjit/lib/parseconf.lua b/include/dnsjit/lib/parseconf.lua
new file mode 100644
index 0000000..638763b
--- /dev/null
+++ b/include/dnsjit/lib/parseconf.lua
@@ -0,0 +1,181 @@
+-- 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.lib.parseconf
+-- Parse simple config files
+-- local conf = require("dnsjit.lib.parseconf").new()
+-- .
+-- conf:func("config_name", function(k,...)
+-- print(k,...)
+-- end)
+-- .
+-- conf:file(file)
+-- .
+-- print(conf:val("another_config_name"))
+--
+-- This module parses simple config files that are based on the config
+-- syntax of DSC, drool and parseconf helper library.
+-- Each config begins with a
+-- .B name
+-- followed by
+-- .B options
+-- and ends with a
+-- .BR ; .
+-- Multiple configs can be given on the same line.
+-- Valid option types are
+-- .IR number ,
+-- .IR float ,
+-- .IR string ,
+-- .IR "quoted string" .
+-- Comments can be added by prefixing the comment with
+-- .BR # .
+-- .SS Example
+-- # Comment
+-- number 12345;
+-- float 123.456;
+-- string string string;
+-- quoted_string "string string string";
+-- multi config; on one line;
+module(...,package.seeall)
+
+local log = require("dnsjit.core.log")
+
+local module_log = log.new("lib.parseconf")
+Parseconf = {}
+
+-- Create a new Parseconf object.
+function Parseconf.new()
+ local self = setmetatable({
+ conf = {},
+ cf = {},
+ _log = log.new("lib.parseconf", module_log),
+ }, { __index = Parseconf })
+
+ self._log:debug("new()")
+
+ return self
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Parseconf:log()
+ if self == nil then
+ return module_log
+ end
+ return self._log
+end
+
+-- Set a function to call when config
+-- .I name
+-- is found.
+function Parseconf:func(name, func)
+ self.cf[name] = func
+end
+
+function Parseconf:part(l, n)
+ local p
+ p = l:match("^(%d+)[%s;]", n)
+ if p then
+ return p, tonumber(p)
+ end
+ p = l:match("^(%d+%.%d+)[%s;]", n)
+ if p then
+ return p, tonumber(p)
+ end
+ p = l:match("^(\"[^\"]+\")[%s;]", n)
+ if p then
+ return p, p:sub(2, -2)
+ end
+ p = l:match("^([^%s;]+)[%s;]", n)
+ if p then
+ return p, p
+ end
+end
+
+function Parseconf:next(l, n)
+ local eol = l:match("^%s*;%s*", n)
+ if eol then
+ return true, eol
+ end
+ local ws = l:match("^%s+", n)
+ if ws then
+ return ws
+ end
+ return false
+end
+
+-- Parse the given file.
+function Parseconf:file(fn)
+ local ln, l
+ ln = 1
+ for l in io.lines(fn) do
+ local c = l:find("#")
+ if c then
+ l = l:sub(1, c - 1)
+ end
+ local e, m = pcall(self.line, self, l)
+ if e == false then
+ error("parse error in "..fn.."["..ln.."]: "..m)
+ end
+ ln = ln + 1
+ end
+end
+
+-- Parse the given line.
+function Parseconf:line(l)
+ local n
+ n = 1
+ while n <= l:len() do
+ local c = nil
+ local va = {}
+ while true do
+ local p, v = self:part(l, n)
+ if not p then
+ error("invalid config at character "..n..": "..l:sub(n))
+ end
+ if not c then
+ c = p
+ else
+ table.insert(va, v)
+ end
+ n = n + p:len()
+ local ws, eol = self:next(l, n)
+ if ws == true then
+ if eol then
+ n = n + eol:len()
+ end
+ break
+ elseif ws == false then
+ error("invalid config at character "..n..": "..l:sub(n))
+ end
+ n = n + ws:len()
+ end
+ if self.cf[c] then
+ self.cf[c](c, unpack(va))
+ else
+ self.conf[c] = va
+ end
+ end
+end
+
+-- Get the value of a config
+-- .IR name .
+function Parseconf:val(name)
+ return self.conf[name]
+end
+
+return Parseconf
diff --git a/include/dnsjit/lib/trie.c b/include/dnsjit/lib/trie.c
new file mode 100644
index 0000000..158c5ca
--- /dev/null
+++ b/include/dnsjit/lib/trie.c
@@ -0,0 +1,923 @@
+/*
+ * Copyright (C) 2016-2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The code originated from https://github.com/fanf2/qp/blob/master/qp.c
+ * at revision 5f6d93753.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lib/trie.h"
+
+/*! \brief Error codes used in the library. */
+enum knot_error {
+ KNOT_EOK = 0,
+
+ /* Directly mapped error codes. */
+ KNOT_ENOMEM = -ENOMEM,
+ KNOT_EINVAL = -EINVAL,
+ KNOT_ENOENT = -ENOENT,
+};
+
+#if defined(__i386) || defined(__x86_64) || defined(_M_IX86) \
+ || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN) \
+ && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+
+/*!
+ * \brief Use a pointer alignment hack to save memory.
+ *
+ * When on, isbranch() relies on the fact that in leaf_t the first pointer
+ * is aligned on multiple of 4 bytes and that the flags bitfield is
+ * overlaid over the lowest two bits of that pointer.
+ * Neither is really guaranteed by the C standards; the second part should
+ * be OK with x86_64 ABI and most likely any other little-endian platform.
+ * It would be possible to manipulate the right bits portably, but it would
+ * complicate the code nontrivially. C++ doesn't even guarantee type-punning.
+ * In debug mode we check this works OK when creating a new trie instance.
+ */
+#define FLAGS_HACK 1
+#else
+#define FLAGS_HACK 0
+#endif
+
+typedef unsigned char byte;
+#ifndef uint
+typedef unsigned int uint;
+#define uint uint
+#endif
+typedef uint bitmap_t; /*! Bit-maps, using the range of 1<<0 to 1<<16 (inclusive). */
+
+typedef struct {
+ uint32_t len; // 32 bits are enough for key lengths; probably even 16 bits would be.
+ uint8_t chars[];
+} tkey_t;
+
+/*! \brief Leaf of trie. */
+typedef struct {
+#if !FLAGS_HACK
+ byte flags;
+#endif
+ tkey_t* key; /*!< The pointer must be aligned to 4-byte multiples! */
+ trie_val_t val;
+} leaf_t;
+
+/*! \brief A trie node is either leaf_t or branch_t. */
+typedef union node node_t;
+
+/*!
+ * \brief Branch node of trie.
+ *
+ * - The flags distinguish whether the node is a leaf_t (0), or a branch
+ * testing the more-important nibble (1) or the less-important one (2).
+ * - It stores the index of the byte that the node tests. The combined
+ * value (index*4 + flags) increases in branch nodes as you go deeper
+ * into the trie. All the keys below a branch are identical up to the
+ * nibble identified by the branch. Indices have to be stored because
+ * we skip any branch nodes that would have a single child.
+ * (Consequently, the skipped parts of key have to be validated in a leaf.)
+ * - The bitmap indicates which subtries are present. The present child nodes
+ * are stored in the twigs array (with no holes between them).
+ * - To simplify storing keys that are prefixes of each other, the end-of-string
+ * position is treated as another nibble value, ordered before all others.
+ * That affects the bitmap and twigs fields.
+ *
+ * \note The branch nodes are never allocated individually, but they are
+ * always part of either the root node or the twigs array of the parent.
+ */
+typedef struct {
+#if FLAGS_HACK
+ uint32_t flags : 2,
+ bitmap : 17; /*!< The first bitmap bit is for end-of-string child. */
+#else
+ byte flags;
+ uint32_t bitmap;
+#endif
+ uint32_t index;
+ node_t* twigs;
+} branch_t;
+
+union node {
+ leaf_t leaf;
+ branch_t branch;
+};
+
+struct trie {
+ node_t root; // undefined when weight == 0, see empty_root()
+ size_t weight;
+ knot_mm_t mm;
+};
+
+/* Included from other files */
+
+/** Readability: avoid const-casts in code. */
+static inline void free_const(const void* what)
+{
+ free((void*)what);
+}
+
+static inline void* mm_alloc(knot_mm_t* mm, size_t size)
+{
+ if (mm)
+ return mm->alloc(mm->ctx, size);
+ else
+ return malloc(size);
+}
+
+static inline void mm_free(knot_mm_t* mm, const void* what)
+{
+ if (mm) {
+ if (mm->free)
+ mm->free((void*)what);
+ } else
+ free_const(what);
+}
+
+static void* mm_malloc(void* ctx, size_t n)
+{
+ (void)ctx;
+ return malloc(n);
+}
+
+static void* mm_realloc(knot_mm_t* mm, void* what, size_t size, size_t prev_size)
+{
+ if (mm) {
+ void* p = mm->alloc(mm->ctx, size);
+ if (p == NULL) {
+ return NULL;
+ } else {
+ if (what) {
+ memcpy(p, what,
+ prev_size < size ? prev_size : size);
+ }
+ mm_free(mm, what);
+ return p;
+ }
+ } else {
+ return realloc(what, size);
+ }
+}
+
+static inline void mm_ctx_init(knot_mm_t* mm)
+{
+ mm->ctx = NULL;
+ mm->alloc = mm_malloc;
+ mm->free = free;
+}
+
+/*! \brief Make the root node empty (debug-only). */
+static inline void empty_root(node_t* root)
+{
+#ifndef NDEBUG
+ *root = (node_t) { .branch = {
+ .flags = 3, // invalid value that fits
+ .bitmap = 0,
+ .index = -1,
+ .twigs = NULL } };
+#endif
+}
+
+/*! \brief Check that unportable code works OK (debug-only). */
+static void assert_portability(void)
+{
+#if FLAGS_HACK
+ assert(((union node) { .leaf = {
+ .key = (tkey_t*)(((uint8_t*)NULL) + 1),
+ .val = NULL } })
+ .branch.flags
+ == 1);
+#endif
+}
+
+/*! \brief Propagate error codes. */
+#define ERR_RETURN(x) \
+ do { \
+ int err_code_ = x; \
+ if (unlikely(err_code_ != KNOT_EOK)) \
+ return err_code_; \
+ } while (false)
+
+/*!
+ * \brief Count the number of set bits.
+ *
+ * \TODO This implementation may be relatively slow on some HW.
+ */
+static uint bitmap_weight(bitmap_t w)
+{
+ assert((w & ~((1 << 17) - 1)) == 0); // using the least-important 17 bits
+ return __builtin_popcount(w);
+}
+
+/*! \brief Only keep the lowest bit in the bitmap (least significant -> twigs[0]). */
+static bitmap_t bitmap_lowest_bit(bitmap_t w)
+{
+ assert((w & ~((1 << 17) - 1)) == 0); // using the least-important 17 bits
+ return 1 << __builtin_ctz(w);
+}
+
+/*! \brief Test flags to determine type of this node. */
+static bool isbranch(const node_t* t)
+{
+ uint f = t->branch.flags;
+ assert(f <= 2);
+ return f != 0;
+}
+
+/*! \brief Make a bitmask for testing a branch bitmap. */
+static bitmap_t nibbit(byte k, uint flags)
+{
+ uint shift = (2 - flags) << 2;
+ uint nibble = (k >> shift) & 0xf;
+ return 1 << (nibble + 1 /*because of prefix keys*/);
+}
+
+/*! \brief Extract a nibble from a key and turn it into a bitmask. */
+static bitmap_t twigbit(const node_t* t, const uint8_t* key, uint32_t len)
+{
+ assert(isbranch(t));
+ uint i = t->branch.index;
+
+ if (i >= len)
+ return 1 << 0; // leaf position
+
+ return nibbit((byte)key[i], t->branch.flags);
+}
+
+/*! \brief Test if a branch node has a child indicated by a bitmask. */
+static bool hastwig(const node_t* t, bitmap_t bit)
+{
+ assert(isbranch(t));
+ return t->branch.bitmap & bit;
+}
+
+/*! \brief Compute offset of an existing child in a branch node. */
+static uint twigoff(const node_t* t, bitmap_t b)
+{
+ assert(isbranch(t));
+ return bitmap_weight(t->branch.bitmap & (b - 1));
+}
+
+/*! \brief Get pointer to a particular child of a branch node. */
+static node_t* twig(node_t* t, uint i)
+{
+ assert(isbranch(t));
+ return &t->branch.twigs[i];
+}
+
+/*!
+ * \brief For a branch nod, compute offset of a child and child count.
+ *
+ * Having this separate might be meaningful for performance optimization.
+ */
+#define TWIGOFFMAX(off, max, t, b) \
+ do { \
+ (off) = twigoff((t), (b)); \
+ (max) = bitmap_weight((t)->branch.bitmap); \
+ } while (0)
+
+/*! \brief Simple string comparator. */
+static int key_cmp(const uint8_t* k1, uint32_t k1_len, const uint8_t* k2, uint32_t k2_len)
+{
+ int ret = memcmp(k1, k2, MIN(k1_len, k2_len));
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* Key string is equal, compare lengths. */
+ if (k1_len == k2_len) {
+ return 0;
+ } else if (k1_len < k2_len) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+trie_t* trie_create(knot_mm_t* mm)
+{
+ assert_portability();
+ trie_t* trie = mm_alloc(mm, sizeof(trie_t));
+ if (trie != NULL) {
+ empty_root(&trie->root);
+ trie->weight = 0;
+ if (mm != NULL)
+ trie->mm = *mm;
+ else
+ mm_ctx_init(&trie->mm);
+ }
+ return trie;
+}
+
+/*! \brief Free anything under the trie node, except for the passed pointer itself. */
+static void clear_trie(node_t* trie, knot_mm_t* mm)
+{
+ if (!isbranch(trie)) {
+ mm_free(mm, trie->leaf.key);
+ } else {
+ branch_t* b = &trie->branch;
+ int len = bitmap_weight(b->bitmap);
+ int i;
+ for (i = 0; i < len; ++i)
+ clear_trie(b->twigs + i, mm);
+ mm_free(mm, b->twigs);
+ }
+}
+
+void trie_free(trie_t* tbl)
+{
+ if (tbl == NULL)
+ return;
+ if (tbl->weight)
+ clear_trie(&tbl->root, &tbl->mm);
+ mm_free(&tbl->mm, tbl);
+}
+
+void trie_clear(trie_t* tbl)
+{
+ assert(tbl);
+ if (!tbl->weight)
+ return;
+ clear_trie(&tbl->root, &tbl->mm);
+ empty_root(&tbl->root);
+ tbl->weight = 0;
+}
+
+size_t trie_weight(const trie_t* tbl)
+{
+ assert(tbl);
+ return tbl->weight;
+}
+
+struct found {
+ leaf_t* l; /**< the found leaf (NULL if not found) */
+ branch_t* p; /**< the leaf's parent (if exists) */
+ bitmap_t b; /**< bit-mask with a single bit marking l under p */
+};
+/** Search trie for an item with the given key (equality only). */
+static struct found find_equal(trie_t* tbl, const uint8_t* key, uint32_t len)
+{
+ assert(tbl);
+ struct found ret0;
+ memset(&ret0, 0, sizeof(ret0));
+ if (!tbl->weight)
+ return ret0;
+ /* Current node and parent while descending (returned values basically). */
+ node_t* t = &tbl->root;
+ branch_t* p = NULL;
+ bitmap_t b = 0;
+ while (isbranch(t)) {
+ __builtin_prefetch(t->branch.twigs);
+ b = twigbit(t, key, len);
+ if (!hastwig(t, b))
+ return ret0;
+ p = &t->branch;
+ t = twig(t, twigoff(t, b));
+ }
+ if (key_cmp(key, len, t->leaf.key->chars, t->leaf.key->len) != 0)
+ return ret0;
+ return (struct found) {
+ .l = &t->leaf,
+ .p = p,
+ .b = b,
+ };
+}
+/** Find item with the first key (lexicographical order). */
+static struct found find_first(trie_t* tbl)
+{
+ assert(tbl);
+ if (!tbl->weight) {
+ struct found ret0;
+ memset(&ret0, 0, sizeof(ret0));
+ return ret0;
+ }
+ /* Current node and parent while descending (returned values basically). */
+ node_t* t = &tbl->root;
+ branch_t* p = NULL;
+ while (isbranch(t)) {
+ p = &t->branch;
+ t = &p->twigs[0];
+ }
+ return (struct found) {
+ .l = &t->leaf,
+ .p = p,
+ .b = p ? bitmap_lowest_bit(p->bitmap) : 0,
+ };
+}
+
+trie_val_t* trie_get_try(trie_t* tbl, const uint8_t* key, uint32_t len)
+{
+ struct found found = find_equal(tbl, key, len);
+ return found.l ? &found.l->val : NULL;
+}
+
+trie_val_t* trie_get_first(trie_t* tbl, uint8_t** key, uint32_t* len)
+{
+ struct found found = find_first(tbl);
+ if (!found.l)
+ return NULL;
+ if (key)
+ *key = found.l->key->chars;
+ if (len)
+ *len = found.l->key->len;
+ return &found.l->val;
+}
+
+/*!
+ * \brief Stack of nodes, storing a path down a trie.
+ *
+ * The structure also serves directly as the public trie_it_t type,
+ * in which case it always points to the current leaf, unless we've finished
+ * (i.e. it->len == 0).
+ */
+typedef struct trie_it {
+ node_t** stack; /*!< The stack; malloc is used directly instead of mm. */
+ uint32_t len; /*!< Current length of the stack. */
+ uint32_t alen; /*!< Allocated/available length of the stack. */
+ /*! \brief Initial storage for \a stack; it should fit in many use cases. */
+ node_t* stack_init[60];
+} nstack_t;
+
+/*! \brief Create a node stack containing just the root (or empty). */
+static void ns_init(nstack_t* ns, trie_t* tbl)
+{
+ assert(tbl);
+ ns->stack = ns->stack_init;
+ ns->alen = sizeof(ns->stack_init) / sizeof(ns->stack_init[0]);
+ if (tbl->weight) {
+ ns->len = 1;
+ ns->stack[0] = &tbl->root;
+ } else {
+ ns->len = 0;
+ }
+}
+
+/*! \brief Free inside of the stack, i.e. not the passed pointer itself. */
+static void ns_cleanup(nstack_t* ns)
+{
+ assert(ns && ns->stack);
+ if (likely(ns->stack == ns->stack_init))
+ return;
+ free(ns->stack);
+#ifndef NDEBUG
+ ns->stack = NULL;
+ ns->alen = 0;
+#endif
+}
+
+/*! \brief Allocate more space for the stack. */
+static int ns_longer_alloc(nstack_t* ns)
+{
+ ns->alen *= 2;
+ size_t new_size = sizeof(nstack_t) + ns->alen * sizeof(node_t*);
+ node_t** st;
+ if (ns->stack == ns->stack_init) {
+ st = malloc(new_size);
+ if (st != NULL)
+ memcpy(st, ns->stack, ns->len * sizeof(node_t*));
+ } else {
+ st = realloc(ns->stack, new_size);
+ }
+ if (st == NULL)
+ return KNOT_ENOMEM;
+ ns->stack = st;
+ return KNOT_EOK;
+}
+
+/*! \brief Ensure the node stack can be extended by one. */
+static inline int ns_longer(nstack_t* ns)
+{
+ // get a longer stack if needed
+ if (likely(ns->len < ns->alen))
+ return KNOT_EOK;
+ return ns_longer_alloc(ns); // hand-split the part suitable for inlining
+}
+
+/*!
+ * \brief Find the "branching point" as if searching for a key.
+ *
+ * The whole path to the point is kept on the passed stack;
+ * always at least the root will remain on the top of it.
+ * Beware: the precise semantics of this function is rather tricky.
+ * The top of the stack will contain: the corresponding leaf if exact match is found;
+ * or the immediate node below a branching-point-on-edge or the branching-point itself.
+ *
+ * \param info Set position of the point of first mismatch (in index and flags).
+ * \param first Set the value of the first non-matching character (from trie),
+ * optionally; end-of-string character has value -256 (that's why it's int).
+ * Note: the character is converted to *unsigned* char (i.e. 0..255),
+ * as that's the ordering used in the trie.
+ *
+ * \return KNOT_EOK or KNOT_ENOMEM.
+ */
+static int ns_find_branch(nstack_t* ns, const uint8_t* key, uint32_t len,
+ branch_t* info, int* first)
+{
+ assert(ns && ns->len && info);
+ // First find some leaf with longest matching prefix.
+ while (isbranch(ns->stack[ns->len - 1])) {
+ ERR_RETURN(ns_longer(ns));
+ node_t* t = ns->stack[ns->len - 1];
+ __builtin_prefetch(t->branch.twigs);
+ bitmap_t b = twigbit(t, key, len);
+ // Even if our key is missing from this branch we need to
+ // keep iterating down to a leaf. It doesn't matter which
+ // twig we choose since the keys are all the same up to this
+ // index. Note that blindly using twigoff(t, b) can cause
+ // an out-of-bounds index if it equals twigmax(t).
+ uint i = hastwig(t, b) ? twigoff(t, b) : 0;
+ ns->stack[ns->len++] = twig(t, i);
+ }
+ tkey_t* lkey = ns->stack[ns->len - 1]->leaf.key;
+ // Find index of the first char that differs.
+ uint32_t index = 0;
+ while (index < MIN(len, lkey->len)) {
+ if (key[index] != lkey->chars[index])
+ break;
+ else
+ ++index;
+ }
+ info->index = index;
+ if (first)
+ *first = lkey->len > index ? (unsigned char)lkey->chars[index] : -256;
+ // Find flags: which half-byte has matched.
+ uint flags;
+ if (index == len && len == lkey->len) { // found equivalent key
+ info->flags = flags = 0;
+ goto success;
+ }
+ if (likely(index < MIN(len, lkey->len))) {
+ byte k2 = (byte)lkey->chars[index];
+ byte k1 = (byte)key[index];
+ flags = ((k1 ^ k2) & 0xf0) ? 1 : 2;
+ } else { // one is prefix of another
+ flags = 1;
+ }
+ info->flags = flags;
+ // now go up the trie from the current leaf
+ branch_t* t;
+ do {
+ if (unlikely(ns->len == 1))
+ goto success; // only the root stays on the stack
+ t = (branch_t*)ns->stack[ns->len - 2];
+ if (t->index < index || (t->index == index && t->flags < flags))
+ goto success;
+ --ns->len;
+ } while (true);
+success:
+#ifndef NDEBUG // invariants on successful return
+ assert(ns->len);
+ if (isbranch(ns->stack[ns->len - 1])) {
+ t = &ns->stack[ns->len - 1]->branch;
+ assert(t->index > index || (t->index == index && t->flags >= flags));
+ }
+ if (ns->len > 1) {
+ t = &ns->stack[ns->len - 2]->branch;
+ assert(t->index < index || (t->index == index && (t->flags < flags || (t->flags == 1 && flags == 0))));
+ }
+#endif
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Advance the node stack to the last leaf in the subtree.
+ *
+ * \return KNOT_EOK or KNOT_ENOMEM.
+ */
+static int ns_last_leaf(nstack_t* ns)
+{
+ assert(ns);
+ do {
+ ERR_RETURN(ns_longer(ns));
+ node_t* t = ns->stack[ns->len - 1];
+ if (!isbranch(t))
+ return KNOT_EOK;
+ int lasti = bitmap_weight(t->branch.bitmap) - 1;
+ assert(lasti >= 0);
+ ns->stack[ns->len++] = twig(t, lasti);
+ } while (true);
+}
+
+/*!
+ * \brief Advance the node stack to the first leaf in the subtree.
+ *
+ * \return KNOT_EOK or KNOT_ENOMEM.
+ */
+static int ns_first_leaf(nstack_t* ns)
+{
+ assert(ns && ns->len);
+ do {
+ ERR_RETURN(ns_longer(ns));
+ node_t* t = ns->stack[ns->len - 1];
+ if (!isbranch(t))
+ return KNOT_EOK;
+ ns->stack[ns->len++] = twig(t, 0);
+ } while (true);
+}
+
+/*!
+ * \brief Advance the node stack to the leaf that is previous to the current node.
+ *
+ * \note Prefix leaf under the current node DOES count (if present; perhaps questionable).
+ * \return KNOT_EOK on success, KNOT_ENOENT on not-found, or possibly KNOT_ENOMEM.
+ */
+static int ns_prev_leaf(nstack_t* ns)
+{
+ assert(ns && ns->len > 0);
+
+ node_t* t = ns->stack[ns->len - 1];
+ if (hastwig(t, 1 << 0)) { // the prefix leaf
+ t = twig(t, 0);
+ ERR_RETURN(ns_longer(ns));
+ ns->stack[ns->len++] = t;
+ return KNOT_EOK;
+ }
+
+ do {
+ if (ns->len < 2)
+ return KNOT_ENOENT; // root without empty key has no previous leaf
+ t = ns->stack[ns->len - 1];
+ node_t* p = ns->stack[ns->len - 2];
+ int pindex = t - p->branch.twigs; // index in parent via pointer arithmetic
+ assert(pindex >= 0 && pindex <= 16);
+ if (pindex > 0) { // t isn't the first child -> go down the previous one
+ ns->stack[ns->len - 1] = twig(p, pindex - 1);
+ return ns_last_leaf(ns);
+ }
+ // we've got to go up again
+ --ns->len;
+ } while (true);
+}
+
+/*!
+ * \brief Advance the node stack to the leaf that is successor to the current node.
+ *
+ * \note Prefix leaf or anything else under the current node DOES count.
+ * \return KNOT_EOK on success, KNOT_ENOENT on not-found, or possibly KNOT_ENOMEM.
+ */
+static int ns_next_leaf(nstack_t* ns)
+{
+ assert(ns && ns->len > 0);
+
+ node_t* t = ns->stack[ns->len - 1];
+ if (isbranch(t))
+ return ns_first_leaf(ns);
+ do {
+ if (ns->len < 2)
+ return KNOT_ENOENT; // not found, as no more parent is available
+ t = ns->stack[ns->len - 1];
+ node_t* p = ns->stack[ns->len - 2];
+ int pindex = t - p->branch.twigs; // index in parent via pointer arithmetic
+ assert(pindex >= 0 && pindex <= 16);
+ int pcount = bitmap_weight(p->branch.bitmap);
+ if (pindex + 1 < pcount) { // t isn't the last child -> go down the next one
+ ns->stack[ns->len - 1] = twig(p, pindex + 1);
+ return ns_first_leaf(ns);
+ }
+ // we've got to go up again
+ --ns->len;
+ } while (true);
+}
+
+int trie_get_leq(trie_t* tbl, const uint8_t* key, uint32_t len, trie_val_t** val)
+{
+ assert(tbl && val);
+ *val = NULL; // so on failure we can just return;
+ if (tbl->weight == 0)
+ return KNOT_ENOENT;
+ { // Intentionally un-indented; until end of function, to bound cleanup attr.
+ // First find a key with longest-matching prefix
+ __attribute__((cleanup(ns_cleanup)))
+ nstack_t ns_local;
+ ns_init(&ns_local, tbl);
+ nstack_t* ns = &ns_local;
+ branch_t bp;
+ int un_leaf; // first unmatched character in the leaf
+ ERR_RETURN(ns_find_branch(ns, key, len, &bp, &un_leaf));
+ int un_key = bp.index < len ? (unsigned char)key[bp.index] : -256;
+ node_t* t = ns->stack[ns->len - 1];
+ if (bp.flags == 0) { // found exact match
+ *val = &t->leaf.val;
+ return KNOT_EOK;
+ }
+ // Get t: the last node on matching path
+ if (isbranch(t) && t->branch.index == bp.index && t->branch.flags == bp.flags) {
+ // t is OK
+ } else {
+ // the top of the stack was the first unmatched node -> step up
+ if (ns->len == 1) {
+ // root was unmatched already
+ if (un_key < un_leaf)
+ return KNOT_ENOENT;
+ ERR_RETURN(ns_last_leaf(ns));
+ goto success;
+ }
+ --ns->len;
+ t = ns->stack[ns->len - 1];
+ }
+ // Now we re-do the first "non-matching" step in the trie
+ // but try the previous child if key was less (it may not exist)
+ bitmap_t b = twigbit(t, key, len);
+ int i = hastwig(t, b)
+ ? twigoff(t, b) - (un_key < un_leaf)
+ : twigoff(t, b) - 1 /*twigoff returns successor when !hastwig*/;
+ if (i >= 0) {
+ ERR_RETURN(ns_longer(ns));
+ ns->stack[ns->len++] = twig(t, i);
+ ERR_RETURN(ns_last_leaf(ns));
+ } else {
+ ERR_RETURN(ns_prev_leaf(ns));
+ }
+ success:
+ assert(!isbranch(ns->stack[ns->len - 1]));
+ *val = &ns->stack[ns->len - 1]->leaf.val;
+ return 1;
+ }
+}
+
+/*! \brief Initialize a new leaf, copying the key, and returning failure code. */
+static int mk_leaf(node_t* leaf, const uint8_t* key, uint32_t len, knot_mm_t* mm)
+{
+ tkey_t* k = mm_alloc(mm, sizeof(tkey_t) + len);
+#if FLAGS_HACK
+ assert(((uintptr_t)k) % 4 == 0); // we need an aligned pointer
+#endif
+ if (unlikely(!k))
+ return KNOT_ENOMEM;
+ k->len = len;
+ memcpy(k->chars, key, len);
+ leaf->leaf = (leaf_t)
+ {
+#if !FLAGS_HACK
+ .flags = 0,
+#endif
+ .val = NULL,
+ .key = k
+ };
+ return KNOT_EOK;
+}
+
+trie_val_t* trie_get_ins(trie_t* tbl, const uint8_t* key, uint32_t len)
+{
+ assert(tbl);
+ // First leaf in an empty tbl?
+ if (unlikely(!tbl->weight)) {
+ if (unlikely(mk_leaf(&tbl->root, key, len, &tbl->mm)))
+ return NULL;
+ ++tbl->weight;
+ return &tbl->root.leaf.val;
+ }
+ { // Intentionally un-indented; until end of function, to bound cleanup attr.
+ // Find the branching-point
+ __attribute__((cleanup(ns_cleanup)))
+ nstack_t ns_local;
+ ns_init(&ns_local, tbl);
+ nstack_t* ns = &ns_local;
+ branch_t bp; // branch-point: index and flags signifying the longest common prefix
+ int k2; // the first unmatched character in the leaf
+ if (unlikely(ns_find_branch(ns, key, len, &bp, &k2)))
+ return NULL;
+ node_t* t = ns->stack[ns->len - 1];
+ if (bp.flags == 0) // the same key was already present
+ return &t->leaf.val;
+ node_t leaf;
+ if (unlikely(mk_leaf(&leaf, key, len, &tbl->mm)))
+ return NULL;
+
+ if (isbranch(t) && bp.index == t->branch.index && bp.flags == t->branch.flags) {
+ // The node t needs a new leaf child.
+ bitmap_t b1 = twigbit(t, key, len);
+ assert(!hastwig(t, b1));
+ uint s, m;
+ TWIGOFFMAX(s, m, t, b1); // new child position and original child count
+ node_t* twigs = mm_realloc(&tbl->mm, t->branch.twigs,
+ sizeof(node_t) * (m + 1), sizeof(node_t) * m);
+ if (unlikely(!twigs))
+ goto err_leaf;
+ memmove(twigs + s + 1, twigs + s, sizeof(node_t) * (m - s));
+ twigs[s] = leaf;
+ t->branch.twigs = twigs;
+ t->branch.bitmap |= b1;
+ ++tbl->weight;
+ return &twigs[s].leaf.val;
+ } else {
+// We need to insert a new binary branch with leaf at *t.
+// Note: it works the same for the case where we insert above root t.
+#ifndef NDEBUG
+ if (ns->len > 1) {
+ node_t* pt = ns->stack[ns->len - 2];
+ assert(hastwig(pt, twigbit(pt, key, len)));
+ }
+#endif
+ node_t* twigs = mm_alloc(&tbl->mm, sizeof(node_t) * 2);
+ if (unlikely(!twigs))
+ goto err_leaf;
+ node_t t2 = *t; // Save before overwriting t.
+ t->branch.flags = bp.flags;
+ t->branch.index = bp.index;
+ t->branch.twigs = twigs;
+ bitmap_t b1 = twigbit(t, key, len);
+ bitmap_t b2 = unlikely(k2 == -256) ? (1 << 0) : nibbit(k2, bp.flags);
+ t->branch.bitmap = b1 | b2;
+ *twig(t, twigoff(t, b1)) = leaf;
+ *twig(t, twigoff(t, b2)) = t2;
+ ++tbl->weight;
+ return &twig(t, twigoff(t, b1))->leaf.val;
+ };
+ err_leaf:
+ mm_free(&tbl->mm, leaf.leaf.key);
+ return NULL;
+ }
+}
+
+/*! \brief Apply a function to every trie_val_t*, in order; a recursive solution. */
+static int apply_trie(node_t* t, int (*f)(trie_val_t*, void*), void* d)
+{
+ assert(t);
+ if (!isbranch(t))
+ return f(&t->leaf.val, d);
+ int child_count = bitmap_weight(t->branch.bitmap);
+ int i;
+ for (i = 0; i < child_count; ++i)
+ ERR_RETURN(apply_trie(twig(t, i), f, d));
+ return KNOT_EOK;
+}
+
+int trie_apply(trie_t* tbl, int (*f)(trie_val_t*, void*), void* d)
+{
+ assert(tbl && f);
+ if (!tbl->weight)
+ return KNOT_EOK;
+ return apply_trie(&tbl->root, f, d);
+}
+
+/* These are all thin wrappers around static Tns* functions. */
+trie_it_t* trie_it_begin(trie_t* tbl)
+{
+ assert(tbl);
+ trie_it_t* it = malloc(sizeof(nstack_t));
+ if (!it)
+ return NULL;
+ ns_init(it, tbl);
+ if (it->len == 0) // empty tbl
+ return it;
+ if (ns_first_leaf(it)) {
+ ns_cleanup(it);
+ free(it);
+ return NULL;
+ }
+ return it;
+}
+
+void trie_it_next(trie_it_t* it)
+{
+ assert(it && it->len);
+ if (ns_next_leaf(it) != KNOT_EOK)
+ it->len = 0;
+}
+
+bool trie_it_finished(trie_it_t* it)
+{
+ assert(it);
+ return it->len == 0;
+}
+
+void trie_it_free(trie_it_t* it)
+{
+ if (!it)
+ return;
+ ns_cleanup(it);
+ free(it);
+}
+
+const uint8_t* trie_it_key(trie_it_t* it, size_t* len)
+{
+ assert(it && it->len);
+ node_t* t = it->stack[it->len - 1];
+ assert(!isbranch(t));
+ tkey_t* key = t->leaf.key;
+ if (len)
+ *len = key->len;
+ return key->chars;
+}
+
+trie_val_t* trie_it_val(trie_it_t* it)
+{
+ assert(it && it->len);
+ node_t* t = it->stack[it->len - 1];
+ assert(!isbranch(t));
+ return &t->leaf.val;
+}
diff --git a/include/dnsjit/lib/trie.h b/include/dnsjit/lib/trie.h
new file mode 100644
index 0000000..b8b8b78
--- /dev/null
+++ b/include/dnsjit/lib/trie.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017-2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifndef __dnsjit_contrib_trie_h
+#define __dnsjit_contrib_trie_h
+
+#include <dnsjit/lib/trie.hh>
+
+#ifndef likely
+/*! \brief Optimize for x to be true value. */
+#define likely(x) __builtin_expect((x), 1)
+#endif
+
+#ifndef unlikely
+/*! \brief Optimize for x to be false value. */
+#define unlikely(x) __builtin_expect((x), 0)
+#endif
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b)) /** Minimum of two numbers **/
+
+#endif
diff --git a/include/dnsjit/lib/trie.hh b/include/dnsjit/lib/trie.hh
new file mode 100644
index 0000000..60c8f8a
--- /dev/null
+++ b/include/dnsjit/lib/trie.hh
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017-2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Memory allocation function prototypes. */
+typedef void* (*knot_mm_alloc_t)(void* ctx, size_t len);
+typedef void (*knot_mm_free_t)(void* p);
+
+/*! \brief Memory allocation context. */
+typedef struct knot_mm {
+ void* ctx; /* \note Must be first */
+ knot_mm_alloc_t alloc;
+ knot_mm_free_t free;
+} knot_mm_t;
+
+/*!
+ * \brief Native API of QP-tries:
+ *
+ * - keys are uint8_t strings, not necessarily zero-terminated,
+ * the structure copies the contents of the passed keys
+ * - values are void* pointers, typically you get an ephemeral pointer to it
+ * - key lengths are limited by 2^32-1 ATM
+ */
+
+/*! \brief Element value. */
+typedef void* trie_val_t;
+
+/*! \brief Opaque structure holding a QP-trie. */
+typedef struct trie trie_t;
+
+/*! \brief Opaque type for holding a QP-trie iterator. */
+typedef struct trie_it trie_it_t;
+
+/*! \brief Create a trie instance. Pass NULL to use malloc+free. */
+trie_t* trie_create(knot_mm_t* mm);
+
+/*! \brief Free a trie instance. */
+void trie_free(trie_t* tbl);
+
+/*! \brief Clear a trie instance (make it empty). */
+void trie_clear(trie_t* tbl);
+
+/*! \brief Return the number of keys in the trie. */
+size_t trie_weight(const trie_t* tbl);
+
+/*! \brief Search the trie, returning NULL on failure. */
+trie_val_t* trie_get_try(trie_t* tbl, const uint8_t* key, uint32_t len);
+
+/*!
+ * \brief Return pointer to the minimum. Optionally with key and its length. */
+trie_val_t* trie_get_first(trie_t* tbl, uint8_t** key, uint32_t* len);
+
+/*! \brief Search the trie, inserting NULL trie_val_t on failure. */
+trie_val_t* trie_get_ins(trie_t* tbl, const uint8_t* key, uint32_t len);
+
+/*!
+ * \brief Search for less-or-equal element.
+ *
+ * \param tbl Trie.
+ * \param key Searched key.
+ * \param len Key length.
+ * \param val Must be valid; it will be set to NULL if not found or errored.
+ * \return KNOT_EOK for exact match, 1 for previous, KNOT_ENOENT for not-found,
+ * or KNOT_E*.
+ */
+int trie_get_leq(trie_t* tbl, const uint8_t* key, uint32_t len, trie_val_t** val);
+
+/*!
+ * \brief Apply a function to every trie_val_t, in order.
+ *
+ * \param d Parameter passed as the second argument to f().
+ * \return First nonzero from f() or zero (i.e. KNOT_EOK).
+ */
+int trie_apply(trie_t* tbl, int (*f)(trie_val_t*, void*), void* d);
+
+/*! \brief Create a new iterator pointing to the first element (if any). */
+trie_it_t* trie_it_begin(trie_t* tbl);
+
+/*!
+ * \brief Advance the iterator to the next element.
+ *
+ * Iteration is in ascending lexicographical order.
+ * In particular, the empty string would be considered as the very first.
+ *
+ * \note You may not use this function if the trie's key-set has been modified
+ * during the lifetime of the iterator (modifying values only is OK).
+ */
+void trie_it_next(trie_it_t* it);
+
+/*! \brief Test if the iterator has gone past the last element. */
+bool trie_it_finished(trie_it_t* it);
+
+/*! \brief Free any resources of the iterator. It's OK to call it on NULL. */
+void trie_it_free(trie_it_t* it);
+
+/*!
+ * \brief Return pointer to the key of the current element.
+ *
+ * \note The optional len is uint32_t internally but size_t is better for our usage,
+ * as it is without an additional type conversion.
+ */
+const uint8_t* trie_it_key(trie_it_t* it, size_t* len);
+
+/*! \brief Return pointer to the value of the current element (writable). */
+trie_val_t* trie_it_val(trie_it_t* it);
diff --git a/include/dnsjit/lib/trie.lua b/include/dnsjit/lib/trie.lua
new file mode 100644
index 0000000..f7fee03
--- /dev/null
+++ b/include/dnsjit/lib/trie.lua
@@ -0,0 +1,172 @@
+-- Copyright (c) 2020, CZ.NIC, z.s.p.o.
+-- 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.lib.trie
+-- Prefix-tree data structure which addresses values by strings or byte arrays
+-- .SS Binary-key trie with integer values
+-- local trie = require("dnsjit.lib.trie").new("uint64_t", true, 4)
+-- -- assume we have a bunch of dnsjit.core.object.ip packets to process
+-- for _, pkt in pairs(pkts) do
+-- local node = trie:get_ins(pkt.src)
+-- local value = node:get() -- new nodes' values are initialized to 0
+-- node:set(value + 1)
+-- end
+-- -- iterate over unique IPs and print number of packets received from each
+-- local iter = trie:iter()
+-- local node = iter:node()
+-- local p = require("dnsjit.lib.ip")
+-- while node ~= nil do
+-- local ip_bytes = node:key()
+-- local npkts = tonumber(node:get())
+-- print(ip.tostring(ip_bytes).." sent "..npkts.." packets")
+-- iter:next()
+-- node = iter:node()
+-- end
+-- .SS String-key trie with cdata values
+-- local trie = require("dnsjit.lib.trie").new("core_object_t*")
+-- local obj1 -- assume this contains cdata of type core_object_t*
+-- local node = trie:get_ins("obj1")
+-- node:set(obj1)
+-- node = trie:get_try("obj1")
+-- assert(node:get() == obj1)
+--
+-- Prefix-tree data structure that stores values indexed by strings or byte
+-- arrays, such as IP addresses.
+-- Values of size up to sizeof(size_t) can be stored directly, otherwise
+-- a pointer must be used.
+module(...,package.seeall)
+
+require("dnsjit.lib.trie_h")
+local ffi = require("ffi")
+local C = ffi.C
+local log = require("dnsjit.core.log")
+local module_log = log.new("lib.trie")
+local TrieNode = require("dnsjit.lib.trie.node")
+local TrieIter = require("dnsjit.lib.trie.iter")
+
+Trie = {}
+
+-- Create a new Trie that stores
+-- .I ctype
+-- values as data.
+-- By default, keys are handled as strings.
+-- To use trie with byte arrays, set
+-- .I binary
+-- to true.
+-- Optionally,
+-- .I keylen
+-- may be specified as a default keylen for binary keys.
+-- For string keys, their string length is used by default.
+function Trie.new(ctype, binary, keylen)
+ if ctype == nil then
+ module_log:fatal("missing value ctype")
+ end
+ if ffi.sizeof(ctype) > ffi.sizeof("void *") then
+ module_log:fatal("data type exceeds max size, use a pointer instead")
+ end
+ if keylen ~= nil and not binary then
+ module_log:warning("setting keylen has no effect for string-key tries")
+ end
+
+ local self = setmetatable({
+ obj = C.trie_create(nil),
+ _binary = binary,
+ _keylen = keylen,
+ _ctype = ctype,
+ _log = log.new("lib.trie", module_log),
+ }, { __index = Trie })
+
+ ffi.gc(self.obj, C.trie_free)
+
+ return self
+end
+
+function Trie:_get_keylen(key, keylen)
+ if keylen ~= nil then
+ if type(keylen) == "number" then
+ return keylen
+ else
+ self._log:fatal("keylen must be numeric")
+ end
+ end
+ if not self._binary then
+ if type(key) == "string" then
+ return string.len(key)
+ else
+ self._log:fatal("key must be string when using trie with non-binary keys")
+ end
+ end
+ if not self._keylen or type(self._keylen) ~= "number" then
+ self._log:fatal("default keylen not set or invalid")
+ end
+ return self._keylen
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Trie:log()
+ if self == nil then
+ return module_log
+ end
+ return self._log
+end
+
+-- Clear the trie instance (make it empty).
+function Trie:clear()
+ C.trie_clear(self.obj)
+end
+
+-- Return the number of keys in the trie.
+function Trie:weight()
+ return tonumber(C.trie_weight(self.obj))
+end
+
+-- Search the trie and return nil of failure.
+function Trie:get_try(key, keylen)
+ keylen = self:_get_keylen(key, keylen)
+ local val = C.trie_get_try(self.obj, key, keylen)
+ if val == nil then return nil end
+ val = ffi.cast("trie_val_t *", val)
+ return TrieNode.new(self, val, key, keylen)
+end
+
+-- Search the trie and insert an empty node (with value set to 0) on failure.
+function Trie:get_ins(key, keylen)
+ keylen = self:_get_keylen(key, keylen)
+ local val = C.trie_get_ins(self.obj, key, keylen)
+ val = ffi.cast("trie_val_t *", val)
+ return TrieNode.new(self, val, key, keylen)
+end
+
+-- Return the first node (minimum).
+function Trie:get_first()
+ local key_ptr = ffi.new("uint8_t *[1]")
+ local keylen_ptr = ffi.new("uint32_t[1]")
+ local val = C.trie_get_first(self.obj, key_ptr, keylen_ptr)
+ local keylen = tonumber(keylen_ptr[0])
+ key = key_ptr[0]
+ return TrieNode.new(self, val, key, keylen)
+end
+
+-- Return a trie iterator.
+-- It is only valid as long as the key-set remains unchanged.
+function Trie:iter()
+ return TrieIter.new(self)
+end
+
+-- dnsjit.lib.trie.node (3), dnsjit.lib.trie.iter (3)
+return Trie
diff --git a/include/dnsjit/lib/trie/iter.lua b/include/dnsjit/lib/trie/iter.lua
new file mode 100644
index 0000000..520cc7b
--- /dev/null
+++ b/include/dnsjit/lib/trie/iter.lua
@@ -0,0 +1,93 @@
+-- Copyright (c) 2020, CZ.NIC, z.s.p.o.
+-- 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.lib.trie.iter
+-- Iterator of the trie.
+-- .SS Iterate over all trie's key-value pairs
+-- local trie = require("dnsjit.lib.trie").new("uint64_t")
+-- local iter = trie:iter()
+-- local node = iter:node()
+-- while node ~= nil do
+-- local key = node:key()
+-- local value = tonumber(node:get())
+-- print(key..": "..value)
+-- iter:next()
+-- node = iter:node()
+-- end
+--
+-- Beware that iterator is only valid as long as the trie's key-set
+-- remains unchanged.
+module(...,package.seeall)
+
+require("dnsjit.lib.trie_h")
+local ffi = require("ffi")
+local C = ffi.C
+local log = require("dnsjit.core.log")
+local module_log = log.new("lib.trie.iter")
+local TrieNode = require("dnsjit.lib.trie.node")
+
+TrieIter = {}
+
+-- Create a new iterator pointing to the first element (if any).
+function TrieIter.new(trie)
+ local self = setmetatable({
+ obj = C.trie_it_begin(trie.obj),
+ _trie = trie,
+ _log = log.new("lib.trie.iter", module_log),
+ }, { __index = TrieIter })
+
+ ffi.gc(self.obj, C.trie_it_free)
+
+ return self
+end
+
+-- Return the Log object to control logging of this instance or module.
+function TrieIter:log()
+ if self == nil then
+ return module_log
+ end
+ return self._log
+end
+
+-- Return the node pointer to by the iterator.
+-- Returns nil when iterator has gone past the last element.
+function TrieIter:node()
+ if C.trie_it_finished(self.obj) then
+ return nil
+ end
+
+ local keylen_ptr = ffi.new("size_t[1]")
+ local key = C.trie_it_key(self.obj, keylen_ptr)
+ local keylen = tonumber(keylen_ptr[0])
+
+ local val = C.trie_it_val(self.obj)
+ return TrieNode.new(self._trie, val, key, keylen)
+end
+
+-- Advance the iterator to the next element.
+--
+-- Iteration is in ascending lexicographical order.
+-- Empty string would be considered as the very first.
+--
+-- You may not use this function if the trie's key-set has been modified during the lifetime of the iterator (modifying only values is OK).
+function TrieIter:next()
+ C.trie_it_next(self.obj)
+end
+
+-- dnsjit.lib.trie (3), dnsjit.lib.trie.node (3)
+return TrieIter
diff --git a/include/dnsjit/lib/trie/node.lua b/include/dnsjit/lib/trie/node.lua
new file mode 100644
index 0000000..7fdc39d
--- /dev/null
+++ b/include/dnsjit/lib/trie/node.lua
@@ -0,0 +1,84 @@
+-- Copyright (c) 2020, CZ.NIC, z.s.p.o.
+-- 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.lib.trie.node
+-- Node of trie, which contains the value and key.
+-- .SS Set a node's value.
+-- node:set(42)
+-- .SS Get a node's key and value.
+-- local key = node:key()
+-- local val = node:get()
+module(...,package.seeall)
+
+require("dnsjit.lib.trie_h")
+local ffi = require("ffi")
+local C = ffi.C
+local log = require("dnsjit.core.log")
+local module_log = log.new("lib.trie.node")
+
+TrieNode = {}
+
+-- Create a new node object.
+function TrieNode.new(trie, val, key, keylen)
+ local self = setmetatable({
+ _key = key,
+ _keylen = keylen,
+ _val = val,
+ _trie = trie,
+ _log = log.new("lib.trie.node", module_log),
+ }, { __index = TrieNode })
+
+ return self
+end
+
+-- Return key and keylen of this node.
+-- Key is string or byte array if the trie's
+-- .I
+-- binary
+-- setting is set to true.
+function TrieNode:key()
+ if self._trie._binary then
+ local key = ffi.new("uint8_t[?]", self._keylen)
+ ffi.copy(key, self._key, self._keylen)
+ return key, self._keylen
+ else
+ return ffi.string(self._key, self._keylen), self._keylen
+ end
+end
+
+-- Return the Log object to control logging of this instance or module.
+function TrieNode:log()
+ if self == nil then
+ return module_log
+ end
+ return self._log
+end
+
+-- Get the value of this node.
+function TrieNode:get()
+ return ffi.cast(self._trie._ctype, self._val[0])
+end
+
+-- Set the value of this node.
+function TrieNode:set(value)
+ value = ffi.cast('void *', value)
+ self._val[0] = value
+end
+
+-- dnsjit.lib.trie (3)
+return TrieNode
diff --git a/include/dnsjit/output.lua b/include/dnsjit/output.lua
new file mode 100644
index 0000000..43f90c3
--- /dev/null
+++ b/include/dnsjit/output.lua
@@ -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/>.
+
+-- dnsjit.output
+-- Output modules for dnsjit
+--
+-- Output modules used to display DNS message, export to various formats or
+-- replay them against other targets.
+module(...,package.seeall)
+
+-- dnsjit.output.dnscli (3),
+-- dnsjit.output.null (3),
+-- dnsjit.output.pcap (3),
+-- dnsjit.output.respdiff (3),
+-- dnsjit.output.tcpcli (3),
+-- dnsjit.output.tlscli (3),
+-- dnsjit.output.udpcli (3)
+return
diff --git a/include/dnsjit/output/dnscli.c b/include/dnsjit/output/dnscli.c
new file mode 100644
index 0000000..f7c5b5e
--- /dev/null
+++ b/include/dnsjit/output/dnscli.c
@@ -0,0 +1,888 @@
+/*
+ * 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 "output/dnscli.h"
+#include "core/assert.h"
+#include "core/object/dns.h"
+#include "core/object/payload.h"
+#include "core/object/udp.h"
+#include "core/object/tcp.h"
+
+#include <netdb.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
+
+static inline uint16_t _need16(const void* ptr)
+{
+ uint16_t v;
+ memcpy(&v, ptr, sizeof(v));
+ return be16toh(v);
+}
+
+static core_log_t _log = LOG_T_INIT("output.dnscli");
+static output_dnscli_t _defaults = {
+ LOG_T_INIT_OBJ("output.dnscli"),
+ OUTPUT_DNSCLI_MODE_NONE,
+ 0, 0, 0, -1, 0, 0,
+ { 0, 0, 0 }, 0,
+ { 0 }, 0,
+ { 0 }, CORE_OBJECT_PAYLOAD_INIT(0), 0, 0, 0, 0, 0,
+ { 0, 0 },
+ 0, 0
+};
+
+core_log_t* output_dnscli_log()
+{
+ return &_log;
+}
+
+void output_dnscli_init(output_dnscli_t* self, output_dnscli_mode_t mode)
+{
+ mlassert_self();
+
+ *self = _defaults;
+ self->mode = mode;
+ self->pkt.payload = self->recvbuf;
+
+ switch (mode & OUTPUT_DNSCLI_MODE_MODES) {
+ case OUTPUT_DNSCLI_MODE_UDP:
+ case OUTPUT_DNSCLI_MODE_TCP:
+ break;
+ case OUTPUT_DNSCLI_MODE_TLS: {
+ int err;
+ if ((err = gnutls_certificate_allocate_credentials(&self->cred)) != GNUTLS_E_SUCCESS) {
+ lfatal("gnutls_certificate_allocate_credentials() error: %s", gnutls_strerror(err));
+ } else if ((err = gnutls_init(&self->session, GNUTLS_CLIENT | ((mode & OUTPUT_DNSCLI_MODE_NONBLOCKING) ? GNUTLS_NONBLOCK : 0))) != GNUTLS_E_SUCCESS) {
+ lfatal("gnutls_init() error: %s", gnutls_strerror(err));
+ } else if ((err = gnutls_set_default_priority(self->session)) != GNUTLS_E_SUCCESS) {
+ lfatal("gnutls_set_default_priority() error: %s", gnutls_strerror(err));
+ } else if ((err = gnutls_credentials_set(self->session, GNUTLS_CRD_CERTIFICATE, self->cred)) != GNUTLS_E_SUCCESS) {
+ lfatal("gnutls_credentials_set() error: %s", gnutls_strerror(err));
+ }
+
+ gnutls_handshake_set_timeout(self->session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+ break;
+ }
+ default:
+ lfatal("Invalid mode %x", mode);
+ }
+}
+
+void output_dnscli_destroy(output_dnscli_t* self)
+{
+ mlassert_self();
+
+ if (self->fd > -1) {
+ switch (self->mode & OUTPUT_DNSCLI_MODE_MODES) {
+ case OUTPUT_DNSCLI_MODE_UDP:
+ case OUTPUT_DNSCLI_MODE_TCP:
+ shutdown(self->fd, SHUT_RDWR);
+ close(self->fd);
+ break;
+ case OUTPUT_DNSCLI_MODE_TLS:
+ if (self->session) {
+ gnutls_bye(self->session, GNUTLS_SHUT_RDWR);
+ gnutls_deinit(self->session);
+ }
+ shutdown(self->fd, SHUT_RDWR);
+ close(self->fd);
+ if (self->cred) {
+ gnutls_certificate_free_credentials(self->cred);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+int output_dnscli_connect(output_dnscli_t* self, const char* host, const char* port)
+{
+ struct addrinfo* addr;
+ int err;
+ mlassert_self();
+ lassert(host, "host is nil");
+ lassert(port, "port is nil");
+
+ if (self->fd > -1) {
+ lfatal("already connected");
+ }
+
+ if ((err = getaddrinfo(host, port, 0, &addr))) {
+ lcritical("getaddrinfo(%s, %s) error %s", host, port, gai_strerror(err));
+ return -1;
+ }
+ if (!addr) {
+ lcritical("getaddrinfo failed, no address returned");
+ return -1;
+ }
+
+ switch (self->mode & OUTPUT_DNSCLI_MODE_MODES) {
+ case OUTPUT_DNSCLI_MODE_UDP:
+ memcpy(&self->addr, addr->ai_addr, addr->ai_addrlen);
+ self->addr_len = addr->ai_addrlen;
+ freeaddrinfo(addr);
+
+ if ((self->fd = socket(((struct sockaddr*)&self->addr)->sa_family, SOCK_DGRAM, 0)) < 0) {
+ lcritical("socket() error %s", core_log_errstr(errno));
+ return -2;
+ }
+ break;
+ case OUTPUT_DNSCLI_MODE_TCP:
+ case OUTPUT_DNSCLI_MODE_TLS:
+ if ((self->fd = socket(addr->ai_addr->sa_family, SOCK_STREAM, 0)) < 0) {
+ lcritical("socket() error %s", core_log_errstr(errno));
+ freeaddrinfo(addr);
+ return -2;
+ }
+
+ if (connect(self->fd, addr->ai_addr, addr->ai_addrlen)) {
+ lcritical("connect() error %s", core_log_errstr(errno));
+ freeaddrinfo(addr);
+ return -2;
+ }
+
+ freeaddrinfo(addr);
+ break;
+ default:
+ break;
+ }
+
+ switch (self->mode & OUTPUT_DNSCLI_MODE_MODES) {
+ case OUTPUT_DNSCLI_MODE_UDP:
+ case OUTPUT_DNSCLI_MODE_TCP:
+ if (self->mode & OUTPUT_DNSCLI_MODE_NONBLOCKING) {
+ int flags;
+
+ if ((flags = fcntl(self->fd, F_GETFL)) == -1) {
+ lcritical("fcntl(FL_GETFL) error %s", core_log_errstr(errno));
+ return -3;
+ }
+
+ if (fcntl(self->fd, F_SETFL, flags | O_NONBLOCK)) {
+ lcritical("fcntl(FL_SETFL, %x) error %s", flags, core_log_errstr(errno));
+ return -3;
+ }
+ self->nonblocking = 1;
+ }
+ if (self->timeout.sec > 0 || self->timeout.nsec > 0) {
+ self->poll.fd = self->fd;
+ self->poll_timeout = (self->timeout.sec * 1e3) + (self->timeout.nsec / 1e6); //NOSONAR
+ if (!self->poll_timeout) {
+ self->poll_timeout = 1;
+ }
+ }
+ break;
+ case OUTPUT_DNSCLI_MODE_TLS: {
+ unsigned int ms;
+ gnutls_transport_set_int(self->session, self->fd);
+ ms = (self->timeout.sec * 1000) + (self->timeout.nsec / 1000000);
+ if (!ms && self->timeout.nsec) {
+ ms = 1;
+ }
+ gnutls_record_set_timeout(self->session, ms);
+
+ /* Establish TLS */
+ do {
+ err = gnutls_handshake(self->session);
+ } while (err < 0 && gnutls_error_is_fatal(err) == 0);
+ if (err == GNUTLS_E_PREMATURE_TERMINATION) {
+ lcritical("gnutls_handshake() error: %s", gnutls_strerror(err));
+ return -3;
+ } else if (err < 0) {
+ lcritical("gnutls_handshake() failed: %s (%d)\n", gnutls_strerror(err), err);
+ return -3;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ self->conn_ok = 1;
+ return 0;
+}
+
+inline ssize_t _send_udp(output_dnscli_t* self, const uint8_t* payload, size_t len, size_t sent)
+{
+ ssize_t n;
+
+ if (self->poll_timeout) {
+ self->poll.events = POLLOUT;
+ n = poll(&self->poll, 1, self->poll_timeout);
+ if (n != 1 || !(self->poll.revents & POLLOUT)) {
+ if (!n) {
+ self->timeouts++;
+ return -1;
+ }
+ self->errs++;
+ return -2;
+ }
+ }
+ n = sendto(self->fd, payload + sent, len - sent, 0, (struct sockaddr*)&self->addr, self->addr_len);
+ if (n > -1) {
+ return n;
+ }
+ switch (errno) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ case EINTR:
+ return -1;
+ default:
+ break;
+ }
+ return -2;
+}
+
+static void _receive_udp(output_dnscli_t* self, const core_object_t* obj)
+{
+ const uint8_t* payload;
+ size_t len, sent = 0;
+ ssize_t n;
+ mlassert_self();
+
+ switch (obj->obj_type) {
+ case CORE_OBJECT_DNS:
+ payload = ((core_object_dns_t*)obj)->payload;
+ len = ((core_object_dns_t*)obj)->len;
+
+ if (((core_object_dns_t*)obj)->includes_dnslen) {
+ if (len < 2) {
+ return;
+ }
+ payload += 2;
+ len -= 2;
+ }
+ break;
+ case CORE_OBJECT_PAYLOAD:
+ payload = ((core_object_payload_t*)obj)->payload;
+ len = ((core_object_payload_t*)obj)->len;
+ break;
+ default:
+ return;
+ }
+
+ for (;;) {
+ n = _send_udp(self, payload, len, sent);
+ if (n > -1) {
+ sent += n;
+ if (sent < len) {
+ continue;
+ }
+ self->pkts++;
+ return;
+ }
+ if (n == -1) {
+ if (self->nonblocking) {
+ // TODO: warn?
+ return;
+ }
+ continue;
+ }
+ break;
+ }
+ self->errs++;
+}
+
+inline ssize_t _send_tcp(output_dnscli_t* self, const uint8_t* payload, size_t len, size_t sent)
+{
+ ssize_t n;
+
+ if (self->poll_timeout) {
+ self->poll.events = POLLOUT;
+ n = poll(&self->poll, 1, self->poll_timeout);
+ if (n != 1 || !(self->poll.revents & POLLOUT)) {
+ if (!n) {
+ self->timeouts++;
+ return -1;
+ }
+ self->errs++;
+ return -2;
+ }
+ }
+ n = sendto(self->fd, payload + sent, len - sent, 0, 0, 0);
+ if (n > -1) {
+ return n;
+ }
+ switch (errno) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ case EINTR:
+ return -1;
+ default:
+ break;
+ }
+ return -2;
+}
+
+static void _receive_tcp(output_dnscli_t* self, const core_object_t* obj)
+{
+ const uint8_t* payload;
+ size_t len, sent = 0;
+ ssize_t n;
+ mlassert_self();
+
+ switch (obj->obj_type) {
+ case CORE_OBJECT_DNS:
+ if (!((core_object_dns_t*)obj)->includes_dnslen) {
+ uint16_t dnslen = htons(((core_object_dns_t*)obj)->len);
+ payload = (const uint8_t*)&dnslen;
+ len = sizeof(dnslen);
+
+ for (;;) {
+ n = _send_tcp(self, payload, len, sent);
+ if (n > -1) {
+ sent += n;
+ if (sent < len) {
+ continue;
+ }
+ break;
+ }
+ if (n == -1) {
+ if (self->nonblocking) {
+ // TODO: warn?
+ return;
+ }
+ continue;
+ }
+ self->errs++;
+ return;
+ }
+ sent = 0;
+ }
+ payload = ((core_object_dns_t*)obj)->payload;
+ len = ((core_object_dns_t*)obj)->len;
+ break;
+ case CORE_OBJECT_PAYLOAD:
+ payload = ((core_object_payload_t*)obj)->payload;
+ len = ((core_object_payload_t*)obj)->len;
+ break;
+ default:
+ return;
+ }
+
+ for (;;) {
+ n = _send_tcp(self, payload, len, sent);
+ if (n > -1) {
+ sent += n;
+ if (sent < len) {
+ continue;
+ }
+ self->pkts++;
+ return;
+ }
+ if (n == -1) {
+ if (self->nonblocking) {
+ // TODO: warn?
+ return;
+ }
+ continue;
+ }
+ break;
+ }
+ self->errs++;
+}
+
+inline ssize_t _send_tls(output_dnscli_t* self, const uint8_t* payload, size_t len, size_t sent)
+{
+ ssize_t n;
+
+ n = gnutls_record_send(self->session, payload + sent, len - sent);
+ if (n > -1) {
+ return n;
+ }
+ switch (n) {
+ case GNUTLS_E_AGAIN:
+ case GNUTLS_E_TIMEDOUT:
+ case GNUTLS_E_INTERRUPTED:
+ return -1;
+ default:
+ break;
+ }
+ return -2;
+}
+
+static void _receive_tls(output_dnscli_t* self, const core_object_t* obj)
+{
+ const uint8_t* payload;
+ size_t len, sent = 0;
+ ssize_t n;
+ mlassert_self();
+
+ switch (obj->obj_type) {
+ case CORE_OBJECT_DNS:
+ if (!((core_object_dns_t*)obj)->includes_dnslen) {
+ uint16_t dnslen = htons(((core_object_dns_t*)obj)->len);
+ payload = (const uint8_t*)&dnslen;
+ len = sizeof(dnslen);
+
+ for (;;) {
+ n = _send_tls(self, payload, len, sent);
+ if (n > -1) {
+ sent += n;
+ if (sent < len) {
+ continue;
+ }
+ break;
+ }
+ if (n == -1) {
+ if (self->nonblocking) {
+ // TODO: warn?
+ return;
+ }
+ continue;
+ }
+ self->errs++;
+ return;
+ }
+ sent = 0;
+ }
+ payload = ((core_object_dns_t*)obj)->payload;
+ len = ((core_object_dns_t*)obj)->len;
+ break;
+ case CORE_OBJECT_PAYLOAD:
+ payload = ((core_object_payload_t*)obj)->payload;
+ len = ((core_object_payload_t*)obj)->len;
+ break;
+ default:
+ return;
+ }
+
+ for (;;) {
+ n = _send_tls(self, payload, len, sent);
+ if (n > -1) {
+ sent += n;
+ if (sent < len) {
+ continue;
+ }
+ self->pkts++;
+ return;
+ }
+ if (n == -1) {
+ if (self->nonblocking) {
+ // TODO: warn?
+ return;
+ }
+ continue;
+ }
+ break;
+ }
+ self->errs++;
+}
+
+luajit_ssize_t output_dnscli_send(output_dnscli_t* self, const core_object_t* obj, size_t sent)
+{
+ const uint8_t* payload;
+ size_t len;
+ uint16_t dnslen;
+ mlassert_self();
+
+ switch (self->mode & OUTPUT_DNSCLI_MODE_MODES) {
+ case OUTPUT_DNSCLI_MODE_UDP:
+ switch (obj->obj_type) {
+ case CORE_OBJECT_DNS:
+ payload = ((core_object_dns_t*)obj)->payload;
+ len = ((core_object_dns_t*)obj)->len;
+
+ if (((core_object_dns_t*)obj)->includes_dnslen) {
+ if (len < 2) {
+ return -2;
+ }
+ payload += 2;
+ len -= 2;
+ }
+ break;
+ case CORE_OBJECT_PAYLOAD:
+ payload = ((core_object_payload_t*)obj)->payload;
+ len = ((core_object_payload_t*)obj)->len;
+ break;
+ default:
+ return -2;
+ }
+
+ return _send_udp(self, payload, len, sent);
+
+ case OUTPUT_DNSCLI_MODE_TCP:
+ switch (obj->obj_type) {
+ case CORE_OBJECT_DNS:
+ if (!((core_object_dns_t*)obj)->includes_dnslen) {
+ if (sent < sizeof(dnslen)) {
+ dnslen = htons(((core_object_dns_t*)obj)->len);
+ payload = (const uint8_t*)&dnslen;
+ len = sizeof(dnslen);
+
+ return _send_tcp(self, payload, len, sent);
+ }
+ sent -= sizeof(dnslen);
+ }
+ payload = ((core_object_dns_t*)obj)->payload;
+ len = ((core_object_dns_t*)obj)->len;
+ break;
+ case CORE_OBJECT_PAYLOAD:
+ payload = ((core_object_payload_t*)obj)->payload;
+ len = ((core_object_payload_t*)obj)->len;
+ break;
+ default:
+ return -2;
+ }
+
+ return _send_tcp(self, payload, len, sent);
+
+ case OUTPUT_DNSCLI_MODE_TLS:
+ switch (obj->obj_type) {
+ case CORE_OBJECT_DNS:
+ if (!((core_object_dns_t*)obj)->includes_dnslen) {
+ if (sent < sizeof(dnslen)) {
+ dnslen = htons(((core_object_dns_t*)obj)->len);
+ payload = (const uint8_t*)&dnslen;
+ len = sizeof(dnslen);
+
+ return _send_tls(self, payload, len, sent);
+ }
+ sent -= sizeof(dnslen);
+ }
+ payload = ((core_object_dns_t*)obj)->payload;
+ len = ((core_object_dns_t*)obj)->len;
+ break;
+ case CORE_OBJECT_PAYLOAD:
+ payload = ((core_object_payload_t*)obj)->payload;
+ len = ((core_object_payload_t*)obj)->len;
+ break;
+ default:
+ return -2;
+ }
+
+ return _send_tls(self, payload, len, sent);
+
+ default:
+ break;
+ }
+
+ return -2;
+}
+
+core_receiver_t output_dnscli_receiver(output_dnscli_t* self)
+{
+ mlassert_self();
+
+ if (!self->conn_ok) {
+ lfatal("not connected");
+ }
+
+ switch (self->mode & OUTPUT_DNSCLI_MODE_MODES) {
+ case OUTPUT_DNSCLI_MODE_UDP:
+ return (core_receiver_t)_receive_udp;
+ case OUTPUT_DNSCLI_MODE_TCP:
+ return (core_receiver_t)_receive_tcp;
+ case OUTPUT_DNSCLI_MODE_TLS:
+ return (core_receiver_t)_receive_tls;
+ default:
+ break;
+ }
+
+ lfatal("internal error");
+ return 0;
+}
+
+static const core_object_t* _produce_udp(output_dnscli_t* self)
+{
+ ssize_t n;
+ mlassert_self();
+
+ for (;;) {
+ if (self->poll_timeout) {
+ self->poll.events = POLLIN;
+ n = poll(&self->poll, 1, self->poll_timeout);
+ if (n != 1 || !(self->poll.revents & POLLIN)) {
+ if (!n) {
+ self->timeouts++;
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ } else {
+ self->errs++;
+ }
+ return 0;
+ }
+ }
+ n = recvfrom(self->fd, self->recvbuf, sizeof(self->recvbuf), 0, 0, 0);
+ if (n > -1) {
+ break;
+ }
+ switch (errno) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ case EINTR:
+ if (self->nonblocking) {
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ }
+ continue;
+ default:
+ break;
+ }
+ self->errs++;
+ break;
+ }
+
+ if (n < 1) {
+ return 0;
+ }
+
+ self->pkts_recv++;
+ self->pkt.len = n;
+ return (core_object_t*)&self->pkt;
+}
+
+static const core_object_t* _produce_tcp(output_dnscli_t* self)
+{
+ ssize_t n;
+ mlassert_self();
+
+ if (self->have_pkt) {
+ if (self->recv > self->dnslen + sizeof(self->dnslen)) {
+ self->recv -= self->dnslen + sizeof(self->dnslen);
+ memmove(self->recvbuf, self->recvbuf + self->dnslen + sizeof(self->dnslen), self->recv);
+ } else {
+ self->recv = 0;
+ }
+ self->have_pkt = 0;
+ self->have_dnslen = 0;
+ }
+
+ if (!self->have_dnslen && self->recv >= sizeof(self->dnslen)) {
+ self->dnslen = _need16(self->recvbuf);
+ self->have_dnslen = 1;
+ }
+ if (self->have_dnslen && self->recv >= self->dnslen + sizeof(self->dnslen)) {
+ self->pkts_recv++;
+ self->pkt.len = self->dnslen + sizeof(self->dnslen);
+ self->have_pkt = 1;
+ return (core_object_t*)&self->pkt;
+ }
+
+ for (;;) {
+ if (self->poll_timeout) {
+ self->poll.events = POLLIN;
+ n = poll(&self->poll, 1, self->poll_timeout);
+ if (n != 1 || !(self->poll.revents & POLLIN)) {
+ if (!n) {
+ self->timeouts++;
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ } else {
+ self->errs++;
+ }
+ return 0;
+ }
+ }
+ n = recvfrom(self->fd, self->recvbuf + self->recv, sizeof(self->recvbuf) - self->recv, 0, 0, 0);
+ if (n > 0) {
+ self->recv += n;
+
+ if (!self->have_dnslen && self->recv >= sizeof(self->dnslen)) {
+ self->dnslen = _need16(self->recvbuf);
+ self->have_dnslen = 1;
+ }
+ if (self->have_dnslen && self->recv >= self->dnslen + sizeof(self->dnslen)) {
+ self->pkts_recv++;
+ self->pkt.len = self->dnslen + sizeof(self->dnslen);
+ self->have_pkt = 1;
+ return (core_object_t*)&self->pkt;
+ }
+
+ if (self->nonblocking) {
+ break;
+ }
+ continue;
+ }
+ if (!n) {
+ break;
+ }
+ switch (errno) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ case EINTR:
+ if (self->nonblocking) {
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ }
+ continue;
+ default:
+ break;
+ }
+ self->errs++;
+ break;
+ }
+
+ if (n < 1) {
+ return 0;
+ }
+
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+}
+
+static const core_object_t* _produce_tls(output_dnscli_t* self)
+{
+ ssize_t n;
+ mlassert_self();
+
+ if (self->have_pkt) {
+ if (self->recv > self->dnslen + sizeof(self->dnslen)) {
+ self->recv -= self->dnslen + sizeof(self->dnslen);
+ memmove(self->recvbuf, self->recvbuf + self->dnslen + sizeof(self->dnslen), self->recv);
+ } else {
+ self->recv = 0;
+ }
+ self->have_pkt = 0;
+ self->have_dnslen = 0;
+ }
+
+ if (!self->have_dnslen && self->recv >= sizeof(self->dnslen)) {
+ self->dnslen = _need16(self->recvbuf);
+ self->have_dnslen = 1;
+ }
+ if (self->have_dnslen && self->recv >= self->dnslen + sizeof(self->dnslen)) {
+ self->pkts_recv++;
+ self->pkt.len = self->dnslen + sizeof(self->dnslen);
+ self->have_pkt = 1;
+ return (core_object_t*)&self->pkt;
+ }
+
+ for (;;) {
+ if (!gnutls_record_check_pending(self->session) && self->poll_timeout) {
+ self->poll.events = POLLIN;
+ n = poll(&self->poll, 1, self->poll_timeout);
+ if (n != 1 || !(self->poll.revents & POLLIN)) {
+ if (!n) {
+ self->timeouts++;
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ } else {
+ self->errs++;
+ }
+ return 0;
+ }
+ }
+ n = gnutls_record_recv(self->session, self->recvbuf + self->recv, sizeof(self->recvbuf) - self->recv);
+ if (n > 0) {
+ self->recv += n;
+
+ if (!self->have_dnslen && self->recv >= sizeof(self->dnslen)) {
+ self->dnslen = _need16(self->recvbuf);
+ self->have_dnslen = 1;
+ }
+ if (self->have_dnslen && self->recv >= self->dnslen + sizeof(self->dnslen)) {
+ self->pkts_recv++;
+ self->pkt.len = self->dnslen + sizeof(self->dnslen);
+ self->have_pkt = 1;
+ return (core_object_t*)&self->pkt;
+ }
+
+ if (self->nonblocking) {
+ break;
+ }
+ continue;
+ }
+ if (!n) {
+ break;
+ }
+ switch (n) {
+ case GNUTLS_E_AGAIN:
+ case GNUTLS_E_TIMEDOUT:
+ case GNUTLS_E_INTERRUPTED:
+ if (self->nonblocking) {
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ }
+ continue;
+ default:
+ break;
+ }
+ self->errs++;
+ break;
+ }
+
+ if (n < 1) {
+ return 0;
+ }
+
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+}
+
+core_producer_t output_dnscli_producer(output_dnscli_t* self)
+{
+ mlassert_self();
+
+ if (!self->conn_ok) {
+ lfatal("not connected");
+ }
+
+ switch (self->mode & OUTPUT_DNSCLI_MODE_MODES) {
+ case OUTPUT_DNSCLI_MODE_UDP:
+ return (core_producer_t)_produce_udp;
+ case OUTPUT_DNSCLI_MODE_TCP:
+ return (core_producer_t)_produce_tcp;
+ case OUTPUT_DNSCLI_MODE_TLS:
+ return (core_producer_t)_produce_tls;
+ default:
+ break;
+ }
+
+ lfatal("internal error");
+ return 0;
+}
diff --git a/include/dnsjit/output/dnscli.h b/include/dnsjit/output/dnscli.h
new file mode 100644
index 0000000..9927bd4
--- /dev/null
+++ b/include/dnsjit/output/dnscli.h
@@ -0,0 +1,38 @@
+/*
+ * 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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+#include <dnsjit/core/producer.h>
+#include <dnsjit/core/object/payload.h>
+#include <dnsjit/core/timespec.h>
+#include <dnsjit/core/compat.h>
+
+#ifndef __dnsjit_output_dnscli_h
+#define __dnsjit_output_dnscli_h
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <gnutls/gnutls.h>
+#include <poll.h>
+
+#include <dnsjit/output/dnscli.hh>
+
+#endif
diff --git a/include/dnsjit/output/dnscli.hh b/include/dnsjit/output/dnscli.hh
new file mode 100644
index 0000000..4126423
--- /dev/null
+++ b/include/dnsjit/output/dnscli.hh
@@ -0,0 +1,72 @@
+/*
+ * 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.compat_h")
+//lua:require("dnsjit.core.log")
+//lua:require("dnsjit.core.receiver_h")
+//lua:require("dnsjit.core.producer_h")
+//lua:require("dnsjit.core.object.payload_h")
+//lua:require("dnsjit.core.timespec_h")
+
+typedef enum output_dnscli_mode {
+ OUTPUT_DNSCLI_MODE_NONE = 0,
+ OUTPUT_DNSCLI_MODE_OPTIONS = 0xf,
+ OUTPUT_DNSCLI_MODE_NONBLOCKING = 0x1,
+ OUTPUT_DNSCLI_MODE_MODES = 0xf0,
+ OUTPUT_DNSCLI_MODE_UDP = 0x10,
+ OUTPUT_DNSCLI_MODE_TCP = 0x20,
+ OUTPUT_DNSCLI_MODE_TLS = 0x30,
+} output_dnscli_mode_t;
+
+typedef struct output_dnscli {
+ core_log_t _log;
+
+ output_dnscli_mode_t mode;
+
+ size_t pkts, errs, timeouts;
+ int fd, nonblocking, conn_ok;
+
+ struct pollfd poll;
+ int poll_timeout;
+
+ struct sockaddr_storage addr;
+ size_t addr_len;
+
+ uint8_t recvbuf[(64 * 1024) + 2];
+ core_object_payload_t pkt;
+ uint16_t dnslen;
+ uint8_t have_dnslen, have_pkt;
+ size_t recv, pkts_recv;
+
+ core_timespec_t timeout;
+
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t cred;
+} output_dnscli_t;
+
+core_log_t* output_dnscli_log();
+
+void output_dnscli_init(output_dnscli_t* self, output_dnscli_mode_t mode);
+void output_dnscli_destroy(output_dnscli_t* self);
+int output_dnscli_connect(output_dnscli_t* self, const char* host, const char* port);
+luajit_ssize_t output_dnscli_send(output_dnscli_t* self, const core_object_t* obj, size_t sent);
+
+core_receiver_t output_dnscli_receiver(output_dnscli_t* self);
+core_producer_t output_dnscli_producer(output_dnscli_t* self);
diff --git a/include/dnsjit/output/dnscli.lua b/include/dnsjit/output/dnscli.lua
new file mode 100644
index 0000000..bfdaf36
--- /dev/null
+++ b/include/dnsjit/output/dnscli.lua
@@ -0,0 +1,187 @@
+-- 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.output.dnscli
+-- DNS aware UDP/TCP/TLS client
+-- local dnscli = require("dnsjit.output.dnscli")
+-- .SS UDP Receiver Chain
+-- local output = dnscli.new(dnscli.UDP)
+-- output:connect("127.0.0.1", "53")
+-- input:receiver(output)
+-- .SS TCP Nonblocking
+-- local output = dnscli.new(dnscli.TCP + dnscli.NONBLOCKING)
+-- output:send(object)
+--
+-- The DNS client can a
+-- .I core.object.dns
+-- or a
+-- .I core.object.payload
+-- object via the receiver interface or using
+-- .I send()
+-- and send it as DNS query after which it can receive the response by using
+-- the producer interface.
+-- If the object being sent is a
+-- .I core.object.dns
+-- then it will look at
+-- .I includes_dnslen
+-- attribute and depending on the protocol it will disregard, include or send
+-- the DNS length as an extra packet.
+-- If the object being sent is a
+-- .I core.object.payload
+-- then no special handling will be done and it will be sent as is.
+-- When receiving responses the producer interface will generate
+-- .I core.object.payload
+-- objects which may include the DNS length depending on the protocol used and
+-- must be handled by the caller.
+-- .SS MODES
+-- These transport modes and options are available when creating a new Dnscli
+-- output.
+-- .TP
+-- UDP
+-- Create an output using UDP.
+-- .TP
+-- TCP
+-- Create an output using TCP.
+-- .TP
+-- TLS
+-- Create an output using TCP and encrypt it with TLS.
+-- .TP
+-- NONBLOCKING
+-- Make the client nonblocking, see
+-- .I send()
+-- and
+-- .IR produce() .
+module(...,package.seeall)
+
+require("dnsjit.output.dnscli_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "output_dnscli_t"
+local output_dnscli_t = ffi.typeof(t_name)
+local Dnscli = {
+ NONBLOCKING = 0x1,
+ UDP = 0x10,
+ TCP = 0x20,
+ TLS = 0x30,
+}
+
+-- Create a new Dnscli output.
+function Dnscli.new(mode)
+ local self = {
+ obj = output_dnscli_t(),
+ }
+ C.output_dnscli_init(self.obj, mode)
+ ffi.gc(self.obj, C.output_dnscli_destroy)
+ return setmetatable(self, { __index = Dnscli })
+end
+
+-- Set or return the timeout used for sending and reciving, must be used before
+-- .IR connect() .
+function Dnscli:timeout(seconds, nanoseconds)
+ if seconds == nil and nanoseconds == nil then
+ return self.obj.timeout
+ end
+ if nanoseconds == nil then
+ nanoseconds = 0
+ end
+ self.obj.timeout.sec = seconds
+ self.obj.timeout.nsec = nanoseconds
+end
+
+-- Connect to the
+-- .I host
+-- and
+-- .I port
+-- and return 0 if successful.
+function Dnscli:connect(host, port)
+ return C.output_dnscli_connect(self.obj, host, port)
+end
+
+-- Return if nonblocking mode is on (true) or off (false).
+function Dnscli:nonblocking()
+ if self.obj.nonblocking == 1 then
+ return true
+ end
+ return false
+end
+
+-- Send an object and optionally continue sending after
+-- .I sent
+-- bytes.
+-- Unlike the receive interface this function lets you know if the sending was
+-- successful or not which might be needed on nonblocking connections.
+-- Returns -2 on error, -1 if interrupted, timed out or unable to send due to
+-- nonblocking, or the number of bytes sent.
+-- .B Note
+-- the counters for sent, received, errors and timeouts are not affected by
+-- this function.
+function Dnscli:send(object, sent)
+ if sent == nil then
+ sent = 0
+ end
+ return C.output_dnscli_send(self.obj, object, sent)
+end
+
+-- Return the C functions and context for receiving objects, these objects
+-- will be sent.
+function Dnscli:receive()
+ return C.output_dnscli_receiver(self.obj), self.obj
+end
+
+-- Return the C functions and context for producing objects, these objects
+-- are received.
+-- If nonblocking mode is enabled the producer will return a payload object
+-- with length zero if there was nothing to receive.
+-- If nonblocking mode is disabled the producer will wait for data and if
+-- timed out (see
+-- .IR timeout )
+-- it will return a payload object with length zero.
+-- The producer returns nil on error.
+function Dnscli:produce()
+ return C.output_dnscli_producer(self.obj), self.obj
+end
+
+-- Return the number of "packets" sent, actually the number of completely sent
+-- payloads.
+function Dnscli:packets()
+ return tonumber(self.obj.pkts)
+end
+
+-- Return the number of "packets" received, actually the number of successful
+-- calls to
+-- .IR recvfrom (2)
+-- that returned data.
+function Dnscli:received()
+ return tonumber(self.obj.pkts_recv)
+end
+
+-- Return the number of errors when sending or receiving.
+function Dnscli:errors()
+ return tonumber(self.obj.errs)
+end
+
+-- Return the number of timeouts when sending or receiving.
+function Dnscli:timeouts()
+ return tonumber(self.obj.timeouts)
+end
+
+-- core.object.dns (3),
+-- core.object.payload (3),
+-- core.timespec (3)
+return Dnscli
diff --git a/include/dnsjit/output/null.lua b/include/dnsjit/output/null.lua
new file mode 100644
index 0000000..b0e167f
--- /dev/null
+++ b/include/dnsjit/output/null.lua
@@ -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/>.
+
+-- dnsjit.output.null
+-- Dummy layer to example.output.null
+--
+-- This module has moved to example.output.null, see examples/modules/output-example in
+-- dnsjit source repository.
+module(...,package.seeall)
+
+ok, cls = pcall(require, "example.output.null")
+if not ok then
+ error("You need to install the example module output-example\n" .. cls)
+end
+
+return cls
diff --git a/include/dnsjit/output/pcap.c b/include/dnsjit/output/pcap.c
new file mode 100644
index 0000000..ede9881
--- /dev/null
+++ b/include/dnsjit/output/pcap.c
@@ -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/>.
+ */
+
+#include "config.h"
+
+#include "output/pcap.h"
+#include "core/assert.h"
+#include "core/object/pcap.h"
+
+static core_log_t _log = LOG_T_INIT("output.pcap");
+static output_pcap_t _defaults = {
+ LOG_T_INIT_OBJ("output.pcap"),
+ 0, 0
+};
+
+core_log_t* output_pcap_log()
+{
+ return &_log;
+}
+
+void output_pcap_init(output_pcap_t* self)
+{
+ mlassert_self();
+
+ *self = _defaults;
+}
+
+void output_pcap_destroy(output_pcap_t* self)
+{
+ mlassert_self();
+}
+
+int output_pcap_open(output_pcap_t* self, const char* file, int linktype, int snaplen)
+{
+ mlassert_self();
+ if (self->dumper) {
+ lfatal("PCAP already opened");
+ }
+
+ if (!(self->pcap = pcap_open_dead(linktype, snaplen))) {
+ lcritical("pcap_open_dead() failed");
+ return -1;
+ }
+
+ if (!(self->dumper = pcap_dump_open(self->pcap, file))) {
+ lcritical("pcap_dump_open() error: %s", pcap_geterr(self->pcap));
+ pcap_close(self->pcap);
+ self->pcap = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+void output_pcap_close(output_pcap_t* self)
+{
+ mlassert_self();
+ if (self->dumper) {
+ pcap_dump_close(self->dumper);
+ self->dumper = 0;
+ }
+ if (self->pcap) {
+ pcap_close(self->pcap);
+ self->pcap = 0;
+ }
+}
+
+int output_pcap_have_errors(output_pcap_t* self)
+{
+ mlassert_self();
+ if (self->dumper) {
+ return ferror(pcap_dump_file(self->dumper));
+ }
+ return 0;
+}
+
+static void _receive(output_pcap_t* self, const core_object_t* obj)
+{
+ struct pcap_pkthdr hdr;
+ mlassert_self();
+
+ while (obj) {
+ if (obj->obj_type == CORE_OBJECT_PCAP) {
+ hdr.ts.tv_sec = ((const core_object_pcap_t*)obj)->ts.sec;
+ hdr.ts.tv_usec = ((const core_object_pcap_t*)obj)->ts.nsec / 1000;
+ hdr.caplen = ((const core_object_pcap_t*)obj)->caplen;
+ hdr.len = ((const core_object_pcap_t*)obj)->len;
+
+ pcap_dump((void*)self->dumper, &hdr, ((const core_object_pcap_t*)obj)->bytes);
+ return;
+ }
+ obj = obj->obj_prev;
+ }
+}
+
+core_receiver_t output_pcap_receiver(output_pcap_t* self)
+{
+ if (!self->dumper) {
+ lfatal("PCAP not opened");
+ }
+
+ return (core_receiver_t)_receive;
+}
diff --git a/include/dnsjit/output/pcap.h b/include/dnsjit/output/pcap.h
new file mode 100644
index 0000000..40146c2
--- /dev/null
+++ b/include/dnsjit/output/pcap.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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+#include <dnsjit/core/producer.h>
+
+#ifndef __dnsjit_output_pcap_h
+#define __dnsjit_output_pcap_h
+
+#include <pcap/pcap.h>
+
+#include <dnsjit/output/pcap.hh>
+
+#endif
diff --git a/include/dnsjit/output/pcap.hh b/include/dnsjit/output/pcap.hh
new file mode 100644
index 0000000..6205333
--- /dev/null
+++ b/include/dnsjit/output/pcap.hh
@@ -0,0 +1,42 @@
+/*
+ * 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_dumper {} pcap_dumper_t;
+#endif
+
+//lua:require("dnsjit.core.log")
+//lua:require("dnsjit.core.receiver_h")
+//lua:require("dnsjit.input.pcap_h")
+
+typedef struct output_pcap {
+ core_log_t _log;
+ pcap_t* pcap;
+ pcap_dumper_t* dumper;
+} output_pcap_t;
+
+core_log_t* output_pcap_log();
+void output_pcap_init(output_pcap_t* self);
+void output_pcap_destroy(output_pcap_t* self);
+int output_pcap_open(output_pcap_t* self, const char* file, int linktype, int snaplen);
+void output_pcap_close(output_pcap_t* self);
+int output_pcap_have_errors(output_pcap_t* self);
+
+core_receiver_t output_pcap_receiver(output_pcap_t* self);
diff --git a/include/dnsjit/output/pcap.lua b/include/dnsjit/output/pcap.lua
new file mode 100644
index 0000000..26ae8e7
--- /dev/null
+++ b/include/dnsjit/output/pcap.lua
@@ -0,0 +1,93 @@
+-- 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.output.pcap
+-- Output to a PCAP using libpcap
+-- local output = require("dnsjit.output.pcap").new()
+-- output:open("file.pcap")
+-- ...
+-- output:close()
+--
+-- Output module for writing
+-- .I dnsjit.core.object.pcap
+-- objects to a PCAP,
+module(...,package.seeall)
+
+require("dnsjit.output.pcap_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "output_pcap_t"
+local output_pcap_t = ffi.typeof(t_name)
+local Pcap = {}
+
+-- Create a new Pcap output.
+function Pcap.new()
+ local self = {
+ obj = output_pcap_t(),
+ }
+ C.output_pcap_init(self.obj)
+ ffi.gc(self.obj, C.output_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.output_pcap_log()
+ end
+ return self.obj._log
+end
+
+-- Open the PCAP
+-- .I file
+-- to write to using the
+-- .I linktype
+-- and
+-- .IR snaplen .
+-- Uses
+-- .B pcap_dump_open()
+-- so you can pass "-" to it to open stdout, see it's man-page for more
+-- information.
+-- Returns 0 on success.
+function Pcap:open(file, linktype, snaplen)
+ return C.output_pcap_open(self.obj, file, linktype, snaplen)
+end
+
+-- Close the PCAP.
+function Pcap:close()
+ C.output_pcap_close(self.obj)
+end
+
+-- Return true if the underlying
+-- .I FILE*
+-- indicates that there's been an error.
+function Pcap:have_errors()
+ if C.output_pcap_have_errors(self.obj) == 0 then
+ return false
+ end
+ return true
+end
+
+-- Return the C functions and context for receiving objects.
+function Pcap:receive()
+ return C.output_pcap_receiver(self.obj), self.obj
+end
+
+-- dnsjit.input.pcap (3)
+return Pcap
diff --git a/include/dnsjit/output/respdiff.c b/include/dnsjit/output/respdiff.c
new file mode 100644
index 0000000..834a264
--- /dev/null
+++ b/include/dnsjit/output/respdiff.c
@@ -0,0 +1,298 @@
+/*
+ * 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 "output/respdiff.h"
+#include "core/assert.h"
+#include "core/object/payload.h"
+
+#ifdef HAVE_LMDB_H
+#include <lmdb.h>
+#endif
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <string.h>
+
+static core_log_t _log = LOG_T_INIT("output.respdiff");
+static output_respdiff_t _defaults = {
+ LOG_T_INIT_OBJ("output.respdiff"),
+ 0, 0, 0, 0, 0, 0
+};
+
+core_log_t* output_respdiff_log()
+{
+ return &_log;
+}
+
+void output_respdiff_init(output_respdiff_t* self, const char* path, size_t mapsize)
+{
+ mlassert_self();
+
+ if (!path) {
+ lfatal("path is nil");
+ }
+
+ *self = _defaults;
+
+#ifdef HAVE_LMDB_H
+ if (mkdir(path, 0775) && errno != EEXIST) {
+ lfatal("mkdir(%s) error %s", path, core_log_errstr(errno));
+ }
+ if (mdb_env_create((MDB_env**)&self->env)) {
+ lfatal("mdb_env_create failed");
+ }
+ if (mdb_env_set_mapsize((MDB_env*)self->env, mapsize)) {
+ lfatal("mdb_env_set_mapsize(%lu) failed", mapsize);
+ }
+ if (mdb_env_set_maxdbs((MDB_env*)self->env, 3)) {
+ lfatal("mdb_env_set_maxdbs failed");
+ }
+ if (mdb_env_open((MDB_env*)self->env, path, 0, 0664)) {
+ lfatal("mdb_env_open(%s) failed", path);
+ }
+ if (mdb_txn_begin((MDB_env*)self->env, 0, 0, (MDB_txn**)&self->txn)) {
+ lfatal("mdb_txn_begin failed for queries");
+ }
+ lfatal_oom(self->qdb = calloc(1, sizeof(MDB_dbi)));
+ if (mdb_dbi_open((MDB_txn*)self->txn, "queries", MDB_CREATE, (MDB_dbi*)self->qdb)) {
+ lfatal("mdb_dbi_open failed for queries");
+ }
+ lfatal_oom(self->rdb = calloc(1, sizeof(MDB_dbi)));
+ if (mdb_dbi_open((MDB_txn*)self->txn, "answers", MDB_CREATE, (MDB_dbi*)self->rdb)) {
+ lfatal("mdb_dbi_open failed for responses");
+ }
+ lfatal_oom(self->meta = calloc(1, sizeof(MDB_dbi)));
+ if (mdb_dbi_open((MDB_txn*)self->txn, "meta", MDB_CREATE, (MDB_dbi*)self->meta)) {
+ lfatal("mdb_dbi_open failed for meta");
+ }
+#endif
+}
+
+void output_respdiff_destroy(output_respdiff_t* self)
+{
+ mlassert_self();
+
+#ifdef HAVE_LMDB_H
+ if (self->env) {
+ mdb_env_close((MDB_env*)self->env);
+ }
+ free(self->qdb);
+ free(self->rdb);
+ free(self->meta);
+#endif
+}
+
+#ifdef HAVE_LMDB_H
+static const char* _meta_version = "version";
+static const char* _meta_version_val = "2018-05-21";
+static const char* _meta_servers = "servers";
+static const char* _meta_name0 = "name0";
+static const char* _meta_name1 = "name1";
+static const char* _meta_start_time = "start_time";
+static const char* _meta_end_time = "end_time";
+#endif
+
+void output_respdiff_commit(output_respdiff_t* self, const char* origname, const char* recvname, uint64_t start_time, uint64_t end_time)
+{
+#ifdef HAVE_LMDB_H
+ MDB_val k, v;
+ uint32_t i;
+ int err;
+ mlassert_self();
+ lassert(origname, "origname is nil");
+ lassert(recvname, "recvname is nil");
+
+ k.mv_size = strlen(_meta_version);
+ k.mv_data = (void*)_meta_version;
+ v.mv_size = strlen(_meta_version_val);
+ v.mv_data = (void*)_meta_version_val;
+ if ((err = mdb_put((MDB_txn*)self->txn, (MDB_dbi) * ((MDB_dbi*)self->meta), &k, &v, 0))) {
+ if (err == MDB_MAP_FULL) {
+ lfatal("mdb_put meta.version failed, database is full");
+ } else {
+ lfatal("mdb_put meta.version failed (%d)", err);
+ }
+ }
+
+ k.mv_size = strlen(_meta_servers);
+ k.mv_data = (void*)_meta_servers;
+ i = 2;
+ v.mv_size = 4;
+ v.mv_data = (void*)&i;
+ if ((err = mdb_put((MDB_txn*)self->txn, (MDB_dbi) * ((MDB_dbi*)self->meta), &k, &v, 0))) {
+ if (err == MDB_MAP_FULL) {
+ lfatal("mdb_put meta.servers failed, database is full");
+ } else {
+ lfatal("mdb_put meta.servers failed (%d)", err);
+ }
+ }
+
+ k.mv_size = strlen(_meta_name0);
+ k.mv_data = (void*)_meta_name0;
+ v.mv_size = strlen(origname);
+ v.mv_data = (void*)origname;
+ if ((err = mdb_put((MDB_txn*)self->txn, (MDB_dbi) * ((MDB_dbi*)self->meta), &k, &v, 0))) {
+ if (err == MDB_MAP_FULL) {
+ lfatal("mdb_put meta.name0 failed, database is full");
+ } else {
+ lfatal("mdb_put meta.name0 failed (%d)", err);
+ }
+ }
+
+ k.mv_size = strlen(_meta_name1);
+ k.mv_data = (void*)_meta_name1;
+ v.mv_size = strlen(recvname);
+ v.mv_data = (void*)recvname;
+ if ((err = mdb_put((MDB_txn*)self->txn, (MDB_dbi) * ((MDB_dbi*)self->meta), &k, &v, 0))) {
+ if (err == MDB_MAP_FULL) {
+ lfatal("mdb_put meta.name1 failed, database is full");
+ } else {
+ lfatal("mdb_put meta.name1 failed (%d)", err);
+ }
+ }
+
+ k.mv_size = strlen(_meta_start_time);
+ k.mv_data = (void*)_meta_start_time;
+ i = start_time;
+ v.mv_size = 4;
+ v.mv_data = (void*)&i;
+ if ((err = mdb_put((MDB_txn*)self->txn, (MDB_dbi) * ((MDB_dbi*)self->meta), &k, &v, 0))) {
+ if (err == MDB_MAP_FULL) {
+ lfatal("mdb_put meta.start_time failed, database is full");
+ } else {
+ lfatal("mdb_put meta.start_time failed (%d)", err);
+ }
+ }
+
+ k.mv_size = strlen(_meta_end_time);
+ k.mv_data = (void*)_meta_end_time;
+ i = end_time;
+ v.mv_size = 4;
+ v.mv_data = (void*)&i;
+ if ((err = mdb_put((MDB_txn*)self->txn, (MDB_dbi) * ((MDB_dbi*)self->meta), &k, &v, 0))) {
+ if (err == MDB_MAP_FULL) {
+ lfatal("mdb_put meta.end_time failed, database is full");
+ } else {
+ lfatal("mdb_put meta.end_time failed (%d)", err);
+ }
+ }
+
+ if (self->txn) {
+ if (mdb_txn_commit((MDB_txn*)self->txn)) {
+ lfatal("mdb_txn_commit failed");
+ }
+ self->txn = 0;
+ }
+#endif
+}
+
+#ifdef HAVE_LMDB_H
+static void _receive(output_respdiff_t* self, const core_object_t* obj)
+{
+ const core_object_payload_t *query, *original, *response;
+ MDB_val k, v;
+ uint8_t responses[132096];
+ uint32_t msec;
+ uint16_t dnslen;
+ int err;
+ mlassert_self();
+
+ if (!obj || obj->obj_type != CORE_OBJECT_PAYLOAD) {
+ lfatal("invalid first object");
+ }
+ query = (core_object_payload_t*)obj;
+
+ if (!query->obj_prev || query->obj_prev->obj_type != CORE_OBJECT_PAYLOAD) {
+ lfatal("invalid second object");
+ }
+ original = (core_object_payload_t*)query->obj_prev;
+
+ response = (core_object_payload_t*)original->obj_prev;
+ if (response && response->obj_type != CORE_OBJECT_PAYLOAD) {
+ lfatal("invalid third object");
+ }
+
+ if (12 + original->len + (response ? response->len : 0) > sizeof(responses)) {
+ lfatal("not enough buffer space for responses");
+ }
+
+ self->count++;
+
+ k.mv_size = sizeof(self->id);
+ k.mv_data = (void*)&self->id;
+ v.mv_size = query->len;
+ v.mv_data = (void*)query->payload;
+ if ((err = mdb_put((MDB_txn*)self->txn, (MDB_dbi) * ((MDB_dbi*)self->qdb), &k, &v, 0))) {
+ if (err == MDB_MAP_FULL) {
+ lfatal("mdb_put query failed, database is full");
+ } else {
+ lfatal("mdb_put query failed (%d)", err);
+ }
+ }
+
+ msec = 1; // TODO
+ memcpy(responses, &msec, 4);
+ dnslen = original->len;
+ memcpy(&responses[4], &dnslen, 2);
+ memcpy(&responses[6], original->payload, original->len);
+ if (response) {
+ memcpy(&responses[6 + original->len], &msec, 4);
+ dnslen = response->len;
+ memcpy(&responses[10 + original->len], &dnslen, 2);
+ memcpy(&responses[12 + original->len], response->payload, response->len);
+ } else {
+ msec = 0xffffffff;
+ memcpy(&responses[6 + original->len], &msec, 4);
+ dnslen = 0;
+ memcpy(&responses[10 + original->len], &dnslen, 2);
+ }
+
+ v.mv_size = 12 + original->len + (response ? response->len : 0);
+ v.mv_data = (void*)responses;
+ if ((err = mdb_put((MDB_txn*)self->txn, (MDB_dbi) * ((MDB_dbi*)self->rdb), &k, &v, 0))) {
+ if (err == MDB_MAP_FULL) {
+ lfatal("mdb_put answers failed, database is full");
+ } else {
+ lfatal("mdb_put answers failed (%d)", err);
+ }
+ }
+
+ self->id++;
+}
+
+core_receiver_t output_respdiff_receiver(output_respdiff_t* self)
+{
+ mlassert_self();
+
+ if (!self->txn) {
+ lfatal("no LMDB opened");
+ }
+
+ return (core_receiver_t)_receive;
+}
+#else
+core_receiver_t output_respdiff_receiver(output_respdiff_t* self)
+{
+ mlassert_self();
+ lfatal("no LMDB support");
+ return 0;
+}
+#endif
diff --git a/include/dnsjit/output/respdiff.h b/include/dnsjit/output/respdiff.h
new file mode 100644
index 0000000..207e225
--- /dev/null
+++ b/include/dnsjit/output/respdiff.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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+
+#ifndef __dnsjit_output_respdiff_h
+#define __dnsjit_output_respdiff_h
+
+#include <stdint.h>
+
+#include <dnsjit/output/respdiff.hh>
+
+#endif
diff --git a/include/dnsjit/output/respdiff.hh b/include/dnsjit/output/respdiff.hh
new file mode 100644
index 0000000..44f597e
--- /dev/null
+++ b/include/dnsjit/output/respdiff.hh
@@ -0,0 +1,36 @@
+/*
+ * 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")
+
+typedef struct output_respdiff {
+ core_log_t _log;
+ void * env, *txn, *qdb, *rdb, *meta;
+ uint32_t id;
+ size_t count;
+} output_respdiff_t;
+
+core_log_t* output_respdiff_log();
+void output_respdiff_init(output_respdiff_t* self, const char* path, size_t mapsize);
+void output_respdiff_destroy(output_respdiff_t* self);
+void output_respdiff_commit(output_respdiff_t* self, const char* origname, const char* recvname, uint64_t start_time, uint64_t end_time);
+
+core_receiver_t output_respdiff_receiver(output_respdiff_t* self);
diff --git a/include/dnsjit/output/respdiff.lua b/include/dnsjit/output/respdiff.lua
new file mode 100644
index 0000000..7d17b4a
--- /dev/null
+++ b/include/dnsjit/output/respdiff.lua
@@ -0,0 +1,94 @@
+-- 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.output.respdiff
+-- Output to respdiff LMDB
+-- local output = require("dnsjit.output.respdiff").new("/path/to/lmdb")
+--
+-- Output to an LMDB database (format 2018-05-21) that can be used by respdiff
+-- to compare the responses found in the input data with the responses
+-- received.
+-- The receive function expects to get a chain of 2 or 3
+-- .IR core.object.payload .
+-- For a completed query; The top of the chain is the query, after it the
+-- original response and then the received response.
+-- For a timed out query; The top of the chain is the query, after it the
+-- original response.
+module(...,package.seeall)
+
+require("dnsjit.output.respdiff_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "output_respdiff_t"
+local output_respdiff_t = ffi.typeof(t_name)
+local Respdiff = {}
+
+-- Create a new Respdiff output and created the LMDB database in the directory
+-- .IR path .
+-- The
+-- .I origname
+-- and
+-- .I recvname
+-- are used to populate the meta table, these names should be the same as
+-- what is configured in
+-- .IR respdiff.cfg .
+-- Optional
+-- .I mapsize
+-- can be given to increase the database size beyond the default size of 10MB.
+function Respdiff.new(path, origname, recvname, mapsize)
+ if mapsize == nil then
+ mapsize = 10485760
+ end
+ local self = {
+ obj = output_respdiff_t(),
+ path = path,
+ origname = origname,
+ recvname = recvname,
+ }
+ C.output_respdiff_init(self.obj, path, mapsize)
+ ffi.gc(self.obj, C.output_respdiff_destroy)
+ return setmetatable(self, { __index = Respdiff })
+end
+
+-- Return the Log object to control logging of this instance or module.
+function Respdiff:log()
+ if self == nil then
+ return C.output_respdiff_log()
+ end
+ return self.obj._log
+end
+
+-- Return the C functions and context for receiving objects.
+function Respdiff:receive()
+ return C.output_respdiff_receiver(self.obj), self.obj
+end
+
+-- Commit the LMDB transactions, can not store any more objects after this
+-- call.
+-- The given
+-- .I start_time
+-- and
+-- .I end_time
+-- are used to fill the meta table.
+function Respdiff:commit(start_time, end_time)
+ C.output_respdiff_commit(self.obj, self.origname, self.recvname, start_time, end_time)
+end
+
+-- respdiff " https://gitlab.nic.cz/knot/respdiff"
+return Respdiff
diff --git a/include/dnsjit/output/tcpcli.c b/include/dnsjit/output/tcpcli.c
new file mode 100644
index 0000000..f2b218b
--- /dev/null
+++ b/include/dnsjit/output/tcpcli.c
@@ -0,0 +1,381 @@
+/*
+ * 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 "output/tcpcli.h"
+#include "core/assert.h"
+#include "core/object/dns.h"
+#include "core/object/payload.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <poll.h>
+
+static core_log_t _log = LOG_T_INIT("output.tcpcli");
+static output_tcpcli_t _defaults = {
+ LOG_T_INIT_OBJ("output.tcpcli"),
+ 0, 0, -1,
+ { 0 }, CORE_OBJECT_PAYLOAD_INIT(0),
+ 0, 0, 0, 0,
+ { 5, 0 }, 1
+};
+
+core_log_t* output_tcpcli_log()
+{
+ return &_log;
+}
+
+void output_tcpcli_init(output_tcpcli_t* self)
+{
+ mlassert_self();
+
+ *self = _defaults;
+ self->pkt.payload = self->recvbuf;
+}
+
+void output_tcpcli_destroy(output_tcpcli_t* self)
+{
+ mlassert_self();
+
+ if (self->fd > -1) {
+ shutdown(self->fd, SHUT_RDWR);
+ close(self->fd);
+ }
+}
+
+int output_tcpcli_connect(output_tcpcli_t* self, const char* host, const char* port)
+{
+ struct addrinfo* addr;
+ int err;
+ mlassert_self();
+ lassert(host, "host is nil");
+ lassert(port, "port is nil");
+
+ if (self->fd > -1) {
+ lfatal("already connected");
+ }
+
+ if ((err = getaddrinfo(host, port, 0, &addr))) {
+ lcritical("getaddrinfo(%s, %s) error %s", host, port, gai_strerror(err));
+ return -1;
+ }
+ if (!addr) {
+ lcritical("getaddrinfo failed, no address returned");
+ return -1;
+ }
+
+ if ((self->fd = socket(addr->ai_addr->sa_family, SOCK_STREAM, 0)) < 0) {
+ lcritical("socket() error %s", core_log_errstr(errno));
+ freeaddrinfo(addr);
+ return -2;
+ }
+
+ if (connect(self->fd, addr->ai_addr, addr->ai_addrlen)) {
+ lcritical("connect() error %s", core_log_errstr(errno));
+ freeaddrinfo(addr);
+ return -2;
+ }
+
+ freeaddrinfo(addr);
+ return 0;
+}
+
+int output_tcpcli_nonblocking(output_tcpcli_t* self)
+{
+ int flags;
+ mlassert_self();
+
+ if (self->fd < 0) {
+ lfatal("not connected");
+ }
+
+ flags = fcntl(self->fd, F_GETFL);
+ if (flags != -1) {
+ flags = flags & O_NONBLOCK ? 1 : 0;
+ }
+
+ return flags;
+}
+
+int output_tcpcli_set_nonblocking(output_tcpcli_t* self, int nonblocking)
+{
+ int flags;
+ mlassert_self();
+
+ if (self->fd < 0) {
+ lfatal("not connected");
+ }
+
+ if ((flags = fcntl(self->fd, F_GETFL)) == -1) {
+ lcritical("fcntl(FL_GETFL) error %s", core_log_errstr(errno));
+ return -1;
+ }
+
+ if (nonblocking) {
+ flags |= O_NONBLOCK;
+ self->blocking = 0;
+ } else {
+ flags &= ~O_NONBLOCK;
+ self->blocking = 1;
+ }
+
+ if (fcntl(self->fd, F_SETFL, flags | O_NONBLOCK)) {
+ lcritical("fcntl(FL_SETFL, %x) error %s", flags, core_log_errstr(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void _receive(output_tcpcli_t* self, const core_object_t* obj)
+{
+ const uint8_t* payload;
+ size_t len, sent;
+ uint16_t dnslen;
+ mlassert_self();
+
+ for (; obj;) {
+ switch (obj->obj_type) {
+ case CORE_OBJECT_DNS:
+ obj = obj->obj_prev;
+ continue;
+ case CORE_OBJECT_PAYLOAD:
+ payload = ((core_object_payload_t*)obj)->payload;
+ len = ((core_object_payload_t*)obj)->len;
+ break;
+ default:
+ return;
+ }
+
+ sent = 0;
+ dnslen = htons(len);
+
+ for (;;) {
+ ssize_t ret = sendto(self->fd, ((uint8_t*)&dnslen) + sent, sizeof(dnslen) - sent, 0, 0, 0);
+ if (ret > -1) {
+ sent += ret;
+ if (sent < sizeof(dnslen))
+ continue;
+
+ sent = 0;
+ for (;;) {
+ ssize_t ret = sendto(self->fd, payload + sent, len - sent, 0, 0, 0);
+ if (ret > -1) {
+ sent += ret;
+ if (sent < len)
+ continue;
+ self->pkts++;
+ return;
+ }
+ switch (errno) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ self->errs++;
+ return;
+ }
+ switch (errno) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ self->errs++;
+ break;
+ }
+}
+
+core_receiver_t output_tcpcli_receiver(output_tcpcli_t* self)
+{
+ mlassert_self();
+
+ if (self->fd < 0) {
+ lfatal("not connected");
+ }
+
+ return (core_receiver_t)_receive;
+}
+
+static const core_object_t* _produce(output_tcpcli_t* self)
+{
+ ssize_t n, recv = 0;
+ uint16_t dnslen;
+ struct pollfd p;
+ int to = 0;
+ mlassert_self();
+
+ // Check if last recvfrom() got more then we needed
+ if (!self->have_dnslen && self->recv > self->dnslen) {
+ recv = self->recv - self->dnslen;
+ if (recv < sizeof(dnslen)) {
+ memcpy(((uint8_t*)&dnslen), self->recvbuf + self->dnslen, recv);
+ } else {
+ memcpy(((uint8_t*)&dnslen), self->recvbuf + self->dnslen, sizeof(dnslen));
+
+ if (recv > sizeof(dnslen)) {
+ self->recv = recv - sizeof(dnslen);
+ memmove(self->recvbuf, self->recvbuf + self->dnslen + sizeof(dnslen), self->recv);
+ } else {
+ self->recv = 0;
+ }
+
+ self->dnslen = ntohs(dnslen);
+ self->have_dnslen = 1;
+
+ if (self->recv > self->dnslen) {
+ self->pkts_recv++;
+ self->pkt.len = self->dnslen;
+ self->have_dnslen = 0;
+ return (core_object_t*)&self->pkt;
+ }
+ }
+ }
+
+ if (self->blocking) {
+ p.fd = self->fd;
+ p.events = POLLIN;
+ p.revents = 0;
+ to = (self->timeout.sec * 1e3) + (self->timeout.nsec / 1e6); //NOSONAR
+ if (!to) {
+ to = 1;
+ }
+ }
+
+ if (!self->have_dnslen) {
+ for (;;) {
+ n = poll(&p, 1, to);
+ if (n < 0 || (p.revents & (POLLERR | POLLHUP | POLLNVAL))) {
+ self->errs++;
+ return 0;
+ }
+ if (!n || !(p.revents & POLLIN)) {
+ if (recv) {
+ self->errs++;
+ return 0;
+ }
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ }
+
+ n = recvfrom(self->fd, ((uint8_t*)&dnslen) + recv, sizeof(dnslen) - recv, 0, 0, 0);
+ if (n > 0) {
+ recv += n;
+ if (recv < sizeof(dnslen))
+ continue;
+ break;
+ }
+ if (!n) {
+ break;
+ }
+ switch (errno) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ continue;
+ default:
+ break;
+ }
+ self->errs++;
+ break;
+ }
+
+ if (n < 1) {
+ return 0;
+ }
+
+ self->dnslen = ntohs(dnslen);
+ self->have_dnslen = 1;
+ self->recv = 0;
+ }
+
+ for (;;) {
+ n = poll(&p, 1, to);
+ if (n < 0 || (p.revents & (POLLERR | POLLHUP | POLLNVAL))) {
+ self->errs++;
+ return 0;
+ }
+ if (!n || !(p.revents & POLLIN)) {
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ }
+
+ n = recvfrom(self->fd, self->recvbuf + self->recv, sizeof(self->recvbuf) - self->recv, 0, 0, 0);
+ if (n > 0) {
+ self->recv += n;
+ if (self->recv < self->dnslen)
+ continue;
+ break;
+ }
+ if (!n) {
+ break;
+ }
+ switch (errno) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ default:
+ break;
+ }
+ self->errs++;
+ break;
+ }
+
+ if (n < 1) {
+ return 0;
+ }
+
+ self->pkts_recv++;
+ self->pkt.len = self->dnslen;
+ self->have_dnslen = 0;
+ return (core_object_t*)&self->pkt;
+}
+
+core_producer_t output_tcpcli_producer(output_tcpcli_t* self)
+{
+ mlassert_self();
+
+ if (self->fd < 0) {
+ lfatal("not connected");
+ }
+
+ return (core_producer_t)_produce;
+}
diff --git a/include/dnsjit/output/tcpcli.h b/include/dnsjit/output/tcpcli.h
new file mode 100644
index 0000000..83b7ea4
--- /dev/null
+++ b/include/dnsjit/output/tcpcli.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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+#include <dnsjit/core/producer.h>
+#include <dnsjit/core/object/payload.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_output_tcpcli_h
+#define __dnsjit_output_tcpcli_h
+
+#include <dnsjit/output/tcpcli.hh>
+
+#endif
diff --git a/include/dnsjit/output/tcpcli.hh b/include/dnsjit/output/tcpcli.hh
new file mode 100644
index 0000000..277b0dd
--- /dev/null
+++ b/include/dnsjit/output/tcpcli.hh
@@ -0,0 +1,51 @@
+/*
+ * 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.payload_h")
+//lua:require("dnsjit.core.timespec_h")
+
+typedef struct output_tcpcli {
+ core_log_t _log;
+ size_t pkts, errs;
+ int fd;
+
+ uint8_t recvbuf[64 * 1024];
+ core_object_payload_t pkt;
+ uint16_t dnslen;
+ uint8_t have_dnslen;
+ size_t recv, pkts_recv;
+
+ core_timespec_t timeout;
+ int8_t blocking;
+} output_tcpcli_t;
+
+core_log_t* output_tcpcli_log();
+
+void output_tcpcli_init(output_tcpcli_t* self);
+void output_tcpcli_destroy(output_tcpcli_t* self);
+int output_tcpcli_connect(output_tcpcli_t* self, const char* host, const char* port);
+int output_tcpcli_nonblocking(output_tcpcli_t* self);
+int output_tcpcli_set_nonblocking(output_tcpcli_t* self, int nonblocking);
+
+core_receiver_t output_tcpcli_receiver(output_tcpcli_t* self);
+core_producer_t output_tcpcli_producer(output_tcpcli_t* self);
diff --git a/include/dnsjit/output/tcpcli.lua b/include/dnsjit/output/tcpcli.lua
new file mode 100644
index 0000000..d57de88
--- /dev/null
+++ b/include/dnsjit/output/tcpcli.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.output.tcpcli
+-- Simple, length aware, TCP client
+-- local output = require("dnsjit.output.tcpcli").new("127.0.0.1", "53")
+--
+-- Simple TCP client that takes any payload you give it, sends the length of
+-- the payload as an unsigned 16 bit integer and then sends the payload.
+-- When receiving it will first retrieve the length of the payload as an
+-- unsigned 16 bit integer and it will stall until it gets, even if
+-- nonblocking mode is used.
+-- Then it will retrieve at least that amount of bytes, if nonblocking mode
+-- is used here then it will return a payload object with length zero if
+-- there was nothing to receive or if the full payload have not been received
+-- yet.
+-- Additional calls will continue retrieving the payload.
+-- .SS Attributes
+-- .TP
+-- timeout
+-- A
+-- .I core.timespec
+-- that is used when producing objects.
+module(...,package.seeall)
+
+require("dnsjit.output.tcpcli_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "output_tcpcli_t"
+local output_tcpcli_t = ffi.typeof(t_name)
+local Tcpcli = {}
+
+-- Create a new Tcpcli output.
+function Tcpcli.new()
+ local self = {
+ obj = output_tcpcli_t(),
+ }
+ C.output_tcpcli_init(self.obj)
+ ffi.gc(self.obj, C.output_tcpcli_destroy)
+ return setmetatable(self, { __index = Tcpcli })
+end
+
+-- Set the timeout when producing objects.
+function Tcpcli:timeout(seconds, nanoseconds)
+ self.obj.timeout.sec = seconds
+ self.obj.timeout.nsec = nanoseconds
+end
+
+-- Connect to the
+-- .I host
+-- and
+-- .I port
+-- and return 0 if successful.
+function Tcpcli:connect(host, port)
+ return C.output_tcpcli_connect(self.obj, host, port)
+end
+
+-- Enable (true) or disable (false) nonblocking mode and
+-- return 0 if successful, if
+-- .I bool
+-- is not specified then return if nonblocking mode is on (true) or off (false).
+function Tcpcli:nonblocking(bool)
+ if bool == nil then
+ if C.output_tcpcli_nonblocking(self.obj) == 1 then
+ return true
+ end
+ return false
+ elseif bool == true then
+ return C.output_tcpcli_set_nonblocking(self.obj, 1)
+ else
+ return C.output_tcpcli_set_nonblocking(self.obj, 0)
+ end
+end
+
+-- Return the C functions and context for receiving objects, these objects
+-- will be sent.
+function Tcpcli:receive()
+ return C.output_tcpcli_receiver(self.obj), self.obj
+end
+
+-- Return the C functions and context for producing objects, these objects
+-- are received.
+-- If nonblocking mode is enabled the producer will return a payload object
+-- with length zero if there was nothing to receive or if the full payload
+-- have not been received yet.
+-- If nonblocking mode is disabled the producer will wait for data and if
+-- timed out (see
+-- .IR timeout )
+-- it will return a payload object with length zero.
+-- If a timeout happens during during the first stage, getting the length, it
+-- will fail and return nil.
+-- Additional calls will continue retrieving the payload.
+-- The producer returns nil on error.
+function Tcpcli:produce()
+ return C.output_tcpcli_producer(self.obj), self.obj
+end
+
+-- Return the number of "packets" sent, actually the number of completely sent
+-- payloads.
+function Tcpcli:packets()
+ return tonumber(self.obj.pkts)
+end
+
+-- Return the number of "packets" received, actually the number of completely
+-- received DNS messages.
+function Tcpcli:received()
+ return tonumber(self.obj.pkts_recv)
+end
+
+-- Return the number of errors when sending.
+function Tcpcli:errors()
+ return tonumber(self.obj.errs)
+end
+
+return Tcpcli
diff --git a/include/dnsjit/output/tlscli.c b/include/dnsjit/output/tlscli.c
new file mode 100644
index 0000000..8c5947a
--- /dev/null
+++ b/include/dnsjit/output/tlscli.c
@@ -0,0 +1,345 @@
+/*
+ * 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 "output/tlscli.h"
+#include "core/assert.h"
+#include "core/object/dns.h"
+#include "core/object/payload.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <poll.h>
+
+static core_log_t _log = LOG_T_INIT("output.tlscli");
+static output_tlscli_t _defaults = {
+ LOG_T_INIT_OBJ("output.tlscli"),
+ 0, 0, -1, 0,
+ { 0 }, CORE_OBJECT_PAYLOAD_INIT(0),
+ 0, 0, 0, 0,
+ { 5, 0 },
+ 0, 0
+};
+
+core_log_t* output_tlscli_log()
+{
+ return &_log;
+}
+
+void output_tlscli_init(output_tlscli_t* self)
+{
+ int err;
+ mlassert_self();
+
+ *self = _defaults;
+ self->pkt.payload = self->recvbuf;
+
+ gnutls_global_init();
+ if ((err = gnutls_certificate_allocate_credentials(&self->cred)) != GNUTLS_E_SUCCESS) {
+ lfatal("gnutls_certificate_allocate_credentials() error: %s", gnutls_strerror(err));
+ } else if ((err = gnutls_init(&self->session, GNUTLS_CLIENT)) != GNUTLS_E_SUCCESS) {
+ lfatal("gnutls_init() error: %s", gnutls_strerror(err));
+ } else if ((err = gnutls_set_default_priority(self->session)) != GNUTLS_E_SUCCESS) {
+ lfatal("gnutls_set_default_priority() error: %s", gnutls_strerror(err));
+ } else if ((err = gnutls_credentials_set(self->session, GNUTLS_CRD_CERTIFICATE, self->cred)) != GNUTLS_E_SUCCESS) {
+ lfatal("gnutls_credentials_set() error: %s", gnutls_strerror(err));
+ }
+
+ gnutls_handshake_set_timeout(self->session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+}
+
+void output_tlscli_destroy(output_tlscli_t* self)
+{
+ mlassert_self();
+
+ if (self->fd > -1) {
+ if (self->session) {
+ gnutls_bye(self->session, GNUTLS_SHUT_RDWR);
+ gnutls_deinit(self->session);
+ }
+ shutdown(self->fd, SHUT_RDWR);
+ close(self->fd);
+ if (self->cred) {
+ gnutls_certificate_free_credentials(self->cred);
+ }
+ }
+}
+
+int output_tlscli_connect(output_tlscli_t* self, const char* host, const char* port)
+{
+ struct addrinfo* addr;
+ int err;
+ unsigned int ms;
+ mlassert_self();
+ lassert(host, "host is nil");
+ lassert(port, "port is nil");
+
+ if (self->fd > -1) {
+ lfatal("already connected");
+ }
+ if (self->tls_ok) {
+ lfatal("TLS already established");
+ }
+
+ if ((err = getaddrinfo(host, port, 0, &addr))) {
+ lcritical("getaddrinfo(%s, %s) error %s", host, port, gai_strerror(err));
+ return -1;
+ }
+ if (!addr) {
+ lcritical("getaddrinfo failed, no address returned");
+ return -1;
+ }
+
+ if ((self->fd = socket(addr->ai_addr->sa_family, SOCK_STREAM, 0)) < 0) {
+ lcritical("socket() error %s", core_log_errstr(errno));
+ freeaddrinfo(addr);
+ return -2;
+ }
+
+ if (connect(self->fd, addr->ai_addr, addr->ai_addrlen)) {
+ lcritical("connect() error %s", core_log_errstr(errno));
+ freeaddrinfo(addr);
+ return -2;
+ }
+
+ freeaddrinfo(addr);
+
+ gnutls_transport_set_int(self->session, self->fd);
+ ms = (self->timeout.sec * 1000) + (self->timeout.nsec / 1000000);
+ if (!ms && self->timeout.nsec) {
+ ms = 1;
+ }
+ gnutls_record_set_timeout(self->session, ms);
+
+ /* Establish TLS */
+ do {
+ err = gnutls_handshake(self->session);
+ } while (err < 0 && gnutls_error_is_fatal(err) == 0);
+ if (err == GNUTLS_E_PREMATURE_TERMINATION) {
+ lcritical("gnutls_handshake() error: %s", gnutls_strerror(err));
+ return -3;
+ } else if (err < 0) {
+ lcritical("gnutls_handshake() failed: %s (%d)\n", gnutls_strerror(err), err);
+ return -3;
+ }
+
+ self->tls_ok = 1;
+ return 0;
+}
+
+static void _receive(output_tlscli_t* self, const core_object_t* obj)
+{
+ const uint8_t* payload;
+ size_t len, sent;
+ uint16_t dnslen;
+ ssize_t ret;
+ mlassert_self();
+
+ for (; obj;) {
+ switch (obj->obj_type) {
+ case CORE_OBJECT_DNS:
+ obj = obj->obj_prev;
+ continue;
+ case CORE_OBJECT_PAYLOAD:
+ payload = ((core_object_payload_t*)obj)->payload;
+ len = ((core_object_payload_t*)obj)->len;
+ break;
+ default:
+ return;
+ }
+
+ sent = 0;
+ dnslen = htons(len);
+
+ for (;;) {
+ ret = gnutls_record_send(self->session, ((uint8_t*)&dnslen) + sent, sizeof(dnslen) - sent);
+ if (ret > -1) {
+ sent += ret;
+ if (sent < sizeof(dnslen))
+ continue;
+
+ sent = 0;
+ for (;;) {
+ ret = gnutls_record_send(self->session, payload + sent, len - sent);
+ if (ret > -1) {
+ sent += ret;
+ if (sent < len)
+ continue;
+ self->pkts++;
+ return;
+ }
+ switch (ret) {
+ case GNUTLS_E_AGAIN:
+ case GNUTLS_E_TIMEDOUT:
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ self->errs++;
+ return;
+ }
+ switch (ret) {
+ case GNUTLS_E_AGAIN:
+ case GNUTLS_E_TIMEDOUT:
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ self->errs++;
+ break;
+ }
+}
+
+core_receiver_t output_tlscli_receiver(output_tlscli_t* self)
+{
+ mlassert_self();
+
+ if (self->fd < 0) {
+ lfatal("not connected");
+ }
+ if (!self->tls_ok) {
+ lfatal("TLS is not established");
+ }
+
+ return (core_receiver_t)_receive;
+}
+
+static const core_object_t* _produce(output_tlscli_t* self)
+{
+ ssize_t n, recv = 0;
+ uint16_t dnslen;
+ mlassert_self();
+
+ // Check if last recvfrom() got more then we needed
+ if (!self->have_dnslen && self->recv > self->dnslen) {
+ recv = self->recv - self->dnslen;
+ if (recv < sizeof(dnslen)) {
+ memcpy(((uint8_t*)&dnslen), self->recvbuf + self->dnslen, recv);
+ } else {
+ memcpy(((uint8_t*)&dnslen), self->recvbuf + self->dnslen, sizeof(dnslen));
+
+ if (recv > sizeof(dnslen)) {
+ self->recv = recv - sizeof(dnslen);
+ memmove(self->recvbuf, self->recvbuf + self->dnslen + sizeof(dnslen), self->recv);
+ } else {
+ self->recv = 0;
+ }
+
+ self->dnslen = ntohs(dnslen);
+ self->have_dnslen = 1;
+
+ if (self->recv > self->dnslen) {
+ self->pkts_recv++;
+ self->pkt.len = self->dnslen;
+ self->have_dnslen = 0;
+ return (core_object_t*)&self->pkt;
+ }
+ }
+ }
+
+ if (!self->have_dnslen) {
+ for (;;) {
+ n = gnutls_record_recv(self->session, ((uint8_t*)&dnslen) + recv, sizeof(dnslen) - recv);
+ if (n > 0) {
+ recv += n;
+ if (recv < sizeof(dnslen))
+ continue;
+ break;
+ }
+ if (!n) {
+ break;
+ }
+ switch (n) {
+ case GNUTLS_E_AGAIN:
+ case GNUTLS_E_TIMEDOUT:
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ default:
+ break;
+ }
+ self->errs++;
+ break;
+ }
+
+ if (n < 1) {
+ return 0;
+ }
+
+ self->dnslen = ntohs(dnslen);
+ self->have_dnslen = 1;
+ self->recv = 0;
+ }
+
+ for (;;) {
+ n = gnutls_record_recv(self->session, self->recvbuf + self->recv, sizeof(self->recvbuf) - self->recv);
+ if (n > 0) {
+ self->recv += n;
+ if (self->recv < self->dnslen)
+ continue;
+ break;
+ }
+ if (!n) {
+ break;
+ }
+ switch (n) {
+ case GNUTLS_E_AGAIN:
+ case GNUTLS_E_TIMEDOUT:
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ default:
+ break;
+ }
+ self->errs++;
+ break;
+ }
+
+ if (n < 1) {
+ return 0;
+ }
+
+ self->pkts_recv++;
+ self->pkt.len = self->dnslen;
+ self->have_dnslen = 0;
+ return (core_object_t*)&self->pkt;
+}
+
+core_producer_t output_tlscli_producer(output_tlscli_t* self)
+{
+ mlassert_self();
+
+ if (self->fd < 0) {
+ lfatal("not connected");
+ }
+ if (!self->tls_ok) {
+ lfatal("TLS is not established");
+ }
+
+ return (core_producer_t)_produce;
+}
diff --git a/include/dnsjit/output/tlscli.h b/include/dnsjit/output/tlscli.h
new file mode 100644
index 0000000..72e7b9d
--- /dev/null
+++ b/include/dnsjit/output/tlscli.h
@@ -0,0 +1,34 @@
+/*
+ * 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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+#include <dnsjit/core/producer.h>
+#include <dnsjit/core/object/payload.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_output_tlscli_h
+#define __dnsjit_output_tlscli_h
+
+#include <gnutls/gnutls.h>
+
+#include <dnsjit/output/tlscli.hh>
+
+#endif
diff --git a/include/dnsjit/output/tlscli.hh b/include/dnsjit/output/tlscli.hh
new file mode 100644
index 0000000..4a0c142
--- /dev/null
+++ b/include/dnsjit/output/tlscli.hh
@@ -0,0 +1,52 @@
+/*
+ * 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.compat_h")
+//lua:require("dnsjit.core.log")
+//lua:require("dnsjit.core.receiver_h")
+//lua:require("dnsjit.core.producer_h")
+//lua:require("dnsjit.core.object.payload_h")
+//lua:require("dnsjit.core.timespec_h")
+
+typedef struct output_tlscli {
+ core_log_t _log;
+ size_t pkts, errs;
+ int fd, tls_ok;
+
+ uint8_t recvbuf[64 * 1024];
+ core_object_payload_t pkt;
+ uint16_t dnslen;
+ uint8_t have_dnslen;
+ size_t recv, pkts_recv;
+
+ core_timespec_t timeout;
+
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t cred;
+} output_tlscli_t;
+
+core_log_t* output_tlscli_log();
+
+void output_tlscli_init(output_tlscli_t* self);
+void output_tlscli_destroy(output_tlscli_t* self);
+int output_tlscli_connect(output_tlscli_t* self, const char* host, const char* port);
+
+core_receiver_t output_tlscli_receiver(output_tlscli_t* self);
+core_producer_t output_tlscli_producer(output_tlscli_t* self);
diff --git a/include/dnsjit/output/tlscli.lua b/include/dnsjit/output/tlscli.lua
new file mode 100644
index 0000000..e37439b
--- /dev/null
+++ b/include/dnsjit/output/tlscli.lua
@@ -0,0 +1,103 @@
+-- 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.output.tlscli
+-- Simple TLS client
+-- local output = require("dnsjit.output.tlscli").new("127.0.0.1", "853")
+--
+-- Simple TLS client that attempts to do a TLS handshake (without
+-- certificate verification). It behaves the same way as tcpcli, except all
+-- the data is sent over the encrypted channel.
+-- .SS Attributes
+-- .TP
+-- timeout
+-- A
+-- .I core.timespec
+-- that is used when producing objects.
+module(...,package.seeall)
+
+require("dnsjit.output.tlscli_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "output_tlscli_t"
+local output_tlscli_t = ffi.typeof(t_name)
+local Tlscli = {}
+
+-- Create a new Tlscli output.
+function Tlscli.new()
+ local self = {
+ obj = output_tlscli_t(),
+ }
+ C.output_tlscli_init(self.obj)
+ ffi.gc(self.obj, C.output_tlscli_destroy)
+ return setmetatable(self, { __index = Tlscli })
+end
+
+-- Set the timeout when producing objects.
+function Tlscli:timeout(seconds, nanoseconds)
+ self.obj.timeout.sec = seconds
+ self.obj.timeout.nsec = nanoseconds
+end
+
+-- Connect to the
+-- .I host
+-- and
+-- .I port
+-- , perform a TLS handshake and return 0 if successful.
+function Tlscli:connect(host, port)
+ return C.output_tlscli_connect(self.obj, host, port)
+end
+
+-- Return the C functions and context for receiving objects, these objects
+-- will be sent.
+function Tlscli:receive()
+ return C.output_tlscli_receiver(self.obj), self.obj
+end
+
+-- Return the C functions and context for producing objects, these objects
+-- are received.
+-- The producer will wait for data and if timed out (see
+-- .IR timeout )
+-- it will return a payload object with length zero.
+-- If a timeout happens during during the first stage, getting the length, it
+-- will fail and return nil.
+-- Additional calls will continue retrieving the payload.
+-- The producer returns nil on error.
+function Tlscli:produce()
+ return C.output_tlscli_producer(self.obj), self.obj
+end
+
+-- Return the number of "packets" sent, actually the number of completely sent
+-- payloads.
+function Tlscli:packets()
+ return tonumber(self.obj.pkts)
+end
+
+-- Return the number of "packets" received, actually the number of completely
+-- received DNS messages.
+function Tlscli:received()
+ return tonumber(self.obj.pkts_recv)
+end
+
+-- Return the number of errors when sending.
+function Tlscli:errors()
+ return tonumber(self.obj.errs)
+end
+
+return Tlscli
diff --git a/include/dnsjit/output/udpcli.c b/include/dnsjit/output/udpcli.c
new file mode 100644
index 0000000..b207d9c
--- /dev/null
+++ b/include/dnsjit/output/udpcli.c
@@ -0,0 +1,300 @@
+/*
+ * 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 "output/udpcli.h"
+#include "core/assert.h"
+#include "core/object/dns.h"
+#include "core/object/payload.h"
+
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <poll.h>
+
+static core_log_t _log = LOG_T_INIT("output.udpcli");
+static output_udpcli_t _defaults = {
+ LOG_T_INIT_OBJ("output.udpcli"),
+ 0, 0, -1,
+ { 0 }, 0,
+ { 0 }, CORE_OBJECT_PAYLOAD_INIT(0), 0,
+ { 5, 0 }, 1
+};
+
+core_log_t* output_udpcli_log()
+{
+ return &_log;
+}
+
+void output_udpcli_init(output_udpcli_t* self)
+{
+ mlassert_self();
+
+ *self = _defaults;
+ self->pkt.payload = self->recvbuf;
+}
+
+void output_udpcli_destroy(output_udpcli_t* self)
+{
+ mlassert_self();
+
+ if (self->fd > -1) {
+ shutdown(self->fd, SHUT_RDWR);
+ close(self->fd);
+ }
+}
+
+int output_udpcli_connect(output_udpcli_t* self, const char* host, const char* port)
+{
+ struct addrinfo* addr;
+ int err;
+ mlassert_self();
+ lassert(host, "host is nil");
+ lassert(port, "port is nil");
+
+ if (self->fd > -1) {
+ lfatal("already connected");
+ }
+
+ if ((err = getaddrinfo(host, port, 0, &addr))) {
+ lcritical("getaddrinfo(%s, %s) error %s", host, port, gai_strerror(err));
+ return -1;
+ }
+ if (!addr) {
+ lcritical("getaddrinfo failed, no address returned");
+ return -1;
+ }
+
+ memcpy(&self->addr, addr->ai_addr, addr->ai_addrlen);
+ self->addr_len = addr->ai_addrlen;
+ freeaddrinfo(addr);
+
+ if ((self->fd = socket(((struct sockaddr*)&self->addr)->sa_family, SOCK_DGRAM, 0)) < 0) {
+ lcritical("socket() error %s", core_log_errstr(errno));
+ return -2;
+ }
+
+ return 0;
+}
+
+int output_udpcli_nonblocking(output_udpcli_t* self)
+{
+ int flags;
+ mlassert_self();
+
+ if (self->fd < 0) {
+ lfatal("not connected");
+ }
+
+ flags = fcntl(self->fd, F_GETFL);
+ if (flags != -1) {
+ flags = flags & O_NONBLOCK ? 1 : 0;
+ }
+
+ return flags;
+}
+
+int output_udpcli_set_nonblocking(output_udpcli_t* self, int nonblocking)
+{
+ int flags;
+ mlassert_self();
+
+ if (self->fd < 0) {
+ lfatal("not connected");
+ }
+
+ if ((flags = fcntl(self->fd, F_GETFL)) == -1) {
+ lcritical("fcntl(FL_GETFL) error %s", core_log_errstr(errno));
+ return -1;
+ }
+
+ if (nonblocking) {
+ flags |= O_NONBLOCK;
+ self->blocking = 0;
+ } else {
+ flags &= ~O_NONBLOCK;
+ self->blocking = 1;
+ }
+
+ if (fcntl(self->fd, F_SETFL, flags | O_NONBLOCK)) {
+ lcritical("fcntl(FL_SETFL, %x) error %s", flags, core_log_errstr(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void _receive(output_udpcli_t* self, const core_object_t* obj)
+{
+ const uint8_t* payload;
+ size_t len, sent;
+ mlassert_self();
+
+ for (; obj;) {
+ switch (obj->obj_type) {
+ case CORE_OBJECT_DNS:
+ obj = obj->obj_prev;
+ continue;
+ case CORE_OBJECT_PAYLOAD:
+ payload = ((core_object_payload_t*)obj)->payload;
+ len = ((core_object_payload_t*)obj)->len;
+ break;
+ default:
+ return;
+ }
+
+ sent = 0;
+ for (;;) {
+ ssize_t ret = sendto(self->fd, payload + sent, len - sent, 0, (struct sockaddr*)&self->addr, self->addr_len);
+ if (ret > -1) {
+ sent += ret;
+ if (sent < len)
+ continue;
+ self->pkts++;
+ return;
+ }
+ switch (errno) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ self->errs++;
+ break;
+ }
+}
+
+core_receiver_t output_udpcli_receiver(output_udpcli_t* self)
+{
+ mlassert_self();
+
+ if (self->fd < 0) {
+ lfatal("not connected");
+ }
+
+ return (core_receiver_t)_receive;
+}
+
+static const core_object_t* _produce(output_udpcli_t* self)
+{
+ ssize_t n;
+ mlassert_self();
+
+ for (;;) {
+ n = recvfrom(self->fd, self->recvbuf, sizeof(self->recvbuf), 0, 0, 0);
+ if (n > -1) {
+ break;
+ }
+ switch (errno) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ default:
+ break;
+ }
+ self->errs++;
+ break;
+ }
+
+ if (n < 1) {
+ return 0;
+ }
+
+ self->pkts_recv++;
+ self->pkt.len = n;
+ return (core_object_t*)&self->pkt;
+}
+
+static const core_object_t* _produce_block(output_udpcli_t* self)
+{
+ ssize_t n;
+ struct pollfd p;
+ int to;
+ mlassert_self();
+
+ p.fd = self->fd;
+ p.events = POLLIN;
+ p.revents = 0;
+ to = (self->timeout.sec * 1e3) + (self->timeout.nsec / 1e6); //NOSONAR
+ if (!to) {
+ to = 1;
+ }
+
+ n = poll(&p, 1, to);
+ if (n < 0 || (p.revents & (POLLERR | POLLHUP | POLLNVAL))) {
+ self->errs++;
+ return 0;
+ }
+ if (!n || !(p.revents & POLLIN)) {
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ }
+
+ for (;;) {
+ n = recvfrom(self->fd, self->recvbuf, sizeof(self->recvbuf), 0, 0, 0);
+ if (n > -1) {
+ break;
+ }
+ switch (errno) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ self->pkt.len = 0;
+ return (core_object_t*)&self->pkt;
+ default:
+ break;
+ }
+ self->errs++;
+ break;
+ }
+
+ if (n < 1) {
+ return 0;
+ }
+
+ self->pkts_recv++;
+ self->pkt.len = n;
+ return (core_object_t*)&self->pkt;
+}
+
+core_producer_t output_udpcli_producer(output_udpcli_t* self)
+{
+ mlassert_self();
+
+ if (self->fd < 0) {
+ lfatal("not connected");
+ }
+
+ if (self->blocking) {
+ return (core_producer_t)_produce_block;
+ }
+ return (core_producer_t)_produce;
+}
diff --git a/include/dnsjit/output/udpcli.h b/include/dnsjit/output/udpcli.h
new file mode 100644
index 0000000..f783642
--- /dev/null
+++ b/include/dnsjit/output/udpcli.h
@@ -0,0 +1,35 @@
+/*
+ * 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 <dnsjit/core/log.h>
+#include <dnsjit/core/receiver.h>
+#include <dnsjit/core/producer.h>
+#include <dnsjit/core/object/payload.h>
+#include <dnsjit/core/timespec.h>
+
+#ifndef __dnsjit_output_udpcli_h
+#define __dnsjit_output_udpcli_h
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <dnsjit/output/udpcli.hh>
+
+#endif
diff --git a/include/dnsjit/output/udpcli.hh b/include/dnsjit/output/udpcli.hh
new file mode 100644
index 0000000..084a5b6
--- /dev/null
+++ b/include/dnsjit/output/udpcli.hh
@@ -0,0 +1,53 @@
+/*
+ * 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.compat_h")
+//lua:require("dnsjit.core.log")
+//lua:require("dnsjit.core.receiver_h")
+//lua:require("dnsjit.core.producer_h")
+//lua:require("dnsjit.core.object.payload_h")
+//lua:require("dnsjit.core.timespec_h")
+
+typedef struct output_udpcli {
+ core_log_t _log;
+ size_t pkts, errs;
+ int fd;
+
+ struct sockaddr_storage addr;
+ size_t addr_len;
+
+ uint8_t recvbuf[4 * 1024];
+ core_object_payload_t pkt;
+ size_t pkts_recv;
+
+ core_timespec_t timeout;
+ int8_t blocking;
+} output_udpcli_t;
+
+core_log_t* output_udpcli_log();
+
+void output_udpcli_init(output_udpcli_t* self);
+void output_udpcli_destroy(output_udpcli_t* self);
+int output_udpcli_connect(output_udpcli_t* self, const char* host, const char* port);
+int output_udpcli_nonblocking(output_udpcli_t* self);
+int output_udpcli_set_nonblocking(output_udpcli_t* self, int nonblocking);
+
+core_receiver_t output_udpcli_receiver(output_udpcli_t* self);
+core_producer_t output_udpcli_producer(output_udpcli_t* self);
diff --git a/include/dnsjit/output/udpcli.lua b/include/dnsjit/output/udpcli.lua
new file mode 100644
index 0000000..0584725
--- /dev/null
+++ b/include/dnsjit/output/udpcli.lua
@@ -0,0 +1,121 @@
+-- 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.output.udpcli
+-- Simple and dumb UDP DNS client
+-- local output = require("dnsjit.output.udpcli").new("127.0.0.1", "53")
+--
+-- Simple and rather dumb DNS client that takes any payload you give it and
+-- sends the full payload over UDP.
+-- .SS Attributes
+-- .TP
+-- timeout
+-- A
+-- .I core.timespec
+-- that is used when producing objects.
+module(...,package.seeall)
+
+require("dnsjit.output.udpcli_h")
+local ffi = require("ffi")
+local C = ffi.C
+
+local t_name = "output_udpcli_t"
+local output_udpcli_t = ffi.typeof(t_name)
+local Udpcli = {}
+
+-- Create a new Udpcli output.
+function Udpcli.new()
+ local self = {
+ obj = output_udpcli_t(),
+ }
+ C.output_udpcli_init(self.obj)
+ ffi.gc(self.obj, C.output_udpcli_destroy)
+ return setmetatable(self, { __index = Udpcli })
+end
+
+-- Set the timeout when producing objects.
+function Udpcli:timeout(seconds, nanoseconds)
+ self.obj.timeout.sec = seconds
+ self.obj.timeout.nsec = nanoseconds
+end
+
+-- Connect to the
+-- .I host
+-- and
+-- .I port
+-- and return 0 if successful.
+function Udpcli:connect(host, port)
+ return C.output_udpcli_connect(self.obj, host, port)
+end
+
+-- Enable (true) or disable (false) nonblocking mode and
+-- return 0 if successful, if
+-- .I bool
+-- is not specified then return if nonblocking mode is on (true) or off (false).
+function Udpcli:nonblocking(bool)
+ if bool == nil then
+ if C.output_udpcli_nonblocking(self.obj) == 1 then
+ return true
+ end
+ return false
+ elseif bool == true then
+ return C.output_udpcli_set_nonblocking(self.obj, 1)
+ else
+ return C.output_udpcli_set_nonblocking(self.obj, 0)
+ end
+end
+
+-- Return the C functions and context for receiving objects, these objects
+-- will be sent.
+function Udpcli:receive()
+ return C.output_udpcli_receiver(self.obj), self.obj
+end
+
+-- Return the C functions and context for producing objects, these objects
+-- are received.
+-- If nonblocking mode is enabled the producer will return a payload object
+-- with length zero if there was nothing to receive.
+-- If nonblocking mode is disabled the producer will wait for data and if
+-- timed out (see
+-- .IR timeout )
+-- it will return a payload object with length zero.
+-- The producer returns nil on error.
+function Udpcli:produce()
+ return C.output_udpcli_producer(self.obj), self.obj
+end
+
+-- Return the number of "packets" sent, actually the number of completely sent
+-- payloads.
+function Udpcli:packets()
+ return tonumber(self.obj.pkts)
+end
+
+-- Return the number of "packets" received, actually the number of successful
+-- calls to
+-- .IR recvfrom (2)
+-- that returned data.
+function Udpcli:received()
+ return tonumber(self.obj.pkts_recv)
+end
+
+-- Return the number of errors when sending or receiving.
+function Udpcli:errors()
+ return tonumber(self.obj.errs)
+end
+
+return Udpcli
diff --git a/include/dnsjit/test/Makefile.am b/include/dnsjit/test/Makefile.am
new file mode 100644
index 0000000..0556db6
--- /dev/null
+++ b/include/dnsjit/test/Makefile.am
@@ -0,0 +1,52 @@
+# 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/>.
+
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+CLEANFILES = test*.log test*.trs test*.out \
+ *.pcap-dist *.lz4-dist *.zst-dist
+
+TESTS = test1.sh test2.sh test3.sh test4.sh test6.sh test-ipsplit.sh \
+ test-trie.sh test-base64url.sh
+
+test1.sh: dns.pcap-dist dns.pcap.lz4-dist dns.pcap.zst-dist
+
+test2.sh: dns.pcap-dist
+
+test3.sh: dns.pcap-dist
+
+test4.sh: dns.pcap-dist
+
+test6.sh: dns.pcap-dist
+
+test-ipsplit.sh: pellets.pcap-dist dns.pcap-dist
+
+test-trie.sh: pellets.pcap-dist dns.pcap-dist
+
+.pcap.pcap-dist:
+ cp "$<" "$@"
+
+.lz4.lz4-dist:
+ cp "$<" "$@"
+
+.zst.zst-dist:
+ cp "$<" "$@"
+
+EXTRA_DIST = $(TESTS) \
+ dns.pcap pellets.pcap test_ipsplit.lua test_trie.lua test_base64url.lua \
+ test1.gold test2.gold test3.gold test4.gold test_compressupport.lua \
+ dns.pcap.lz4 dns.pcap.zst
diff --git a/include/dnsjit/test/Makefile.in b/include/dnsjit/test/Makefile.in
new file mode 100644
index 0000000..b03a826
--- /dev/null
+++ b/include/dnsjit/test/Makefile.in
@@ -0,0 +1,947 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# 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/>.
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/test
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cflags_warn_all.m4 \
+ $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_compiler_vendor.m4 \
+ $(top_srcdir)/m4/ax_ext.m4 \
+ $(top_srcdir)/m4/ax_gcc_x86_avx_xgetbv.m4 \
+ $(top_srcdir)/m4/ax_gcc_x86_cpuid.m4 \
+ $(top_srcdir)/m4/ax_prepend_flag.m4 \
+ $(top_srcdir)/m4/ax_pthread.m4 \
+ $(top_srcdir)/m4/ax_require_defined.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/src/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__extra_recursive_targets = gcov-recursive
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/test-driver
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPUEXT_FLAGS = @CPUEXT_FLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LUAJIT = @LUAJIT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SIMD_FLAGS = @SIMD_FLAGS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+ax_pthread_config = @ax_pthread_config@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+ck_CFLAGS = @ck_CFLAGS@
+ck_LIBS = @ck_LIBS@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+liblz4_CFLAGS = @liblz4_CFLAGS@
+liblz4_LIBS = @liblz4_LIBS@
+libzstd_CFLAGS = @libzstd_CFLAGS@
+libzstd_LIBS = @libzstd_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+luajit_CFLAGS = @luajit_CFLAGS@
+luajit_LIBS = @luajit_LIBS@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+CLEANFILES = test*.log test*.trs test*.out \
+ *.pcap-dist *.lz4-dist *.zst-dist
+
+TESTS = test1.sh test2.sh test3.sh test4.sh test6.sh test-ipsplit.sh \
+ test-trie.sh test-base64url.sh
+
+EXTRA_DIST = $(TESTS) \
+ dns.pcap pellets.pcap test_ipsplit.lua test_trie.lua test_base64url.lua \
+ test1.gold test2.gold test3.gold test4.gold test_compressupport.lua \
+ dns.pcap.lz4 dns.pcap.zst
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .log .lz4 .lz4-dist .pcap .pcap-dist .test .test$(EXEEXT) .trs .zst .zst-dist
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/test/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/test/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+gcov-local:
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+ fi; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
+ else \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+ fi; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
+ else \
+ color_start= color_end=; \
+ fi; \
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+test1.sh.log: test1.sh
+ @p='test1.sh'; \
+ b='test1.sh'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test2.sh.log: test2.sh
+ @p='test2.sh'; \
+ b='test2.sh'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test3.sh.log: test3.sh
+ @p='test3.sh'; \
+ b='test3.sh'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test4.sh.log: test4.sh
+ @p='test4.sh'; \
+ b='test4.sh'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test6.sh.log: test6.sh
+ @p='test6.sh'; \
+ b='test6.sh'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test-ipsplit.sh.log: test-ipsplit.sh
+ @p='test-ipsplit.sh'; \
+ b='test-ipsplit.sh'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test-trie.sh.log: test-trie.sh
+ @p='test-trie.sh'; \
+ b='test-trie.sh'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test-base64url.sh.log: test-base64url.sh
+ @p='test-base64url.sh'; \
+ b='test-base64url.sh'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+gcov: gcov-am
+
+gcov-am: gcov-local
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: all all-am check check-TESTS check-am clean clean-generic \
+ clean-libtool cscopelist-am ctags-am distclean \
+ distclean-generic distclean-libtool distdir dvi dvi-am gcov-am \
+ gcov-local html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am recheck tags-am \
+ uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+test1.sh: dns.pcap-dist dns.pcap.lz4-dist dns.pcap.zst-dist
+
+test2.sh: dns.pcap-dist
+
+test3.sh: dns.pcap-dist
+
+test4.sh: dns.pcap-dist
+
+test6.sh: dns.pcap-dist
+
+test-ipsplit.sh: pellets.pcap-dist dns.pcap-dist
+
+test-trie.sh: pellets.pcap-dist dns.pcap-dist
+
+.pcap.pcap-dist:
+ cp "$<" "$@"
+
+.lz4.lz4-dist:
+ cp "$<" "$@"
+
+.zst.zst-dist:
+ cp "$<" "$@"
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/include/dnsjit/test/dns.pcap b/include/dnsjit/test/dns.pcap
new file mode 100644
index 0000000..a0e585c
--- /dev/null
+++ b/include/dnsjit/test/dns.pcap
Binary files differ
diff --git a/include/dnsjit/test/dns.pcap.lz4 b/include/dnsjit/test/dns.pcap.lz4
new file mode 100644
index 0000000..d042863
--- /dev/null
+++ b/include/dnsjit/test/dns.pcap.lz4
Binary files differ
diff --git a/include/dnsjit/test/dns.pcap.zst b/include/dnsjit/test/dns.pcap.zst
new file mode 100644
index 0000000..07b43c0
--- /dev/null
+++ b/include/dnsjit/test/dns.pcap.zst
Binary files differ
diff --git a/include/dnsjit/test/pellets.pcap b/include/dnsjit/test/pellets.pcap
new file mode 100644
index 0000000..ef39a7b
--- /dev/null
+++ b/include/dnsjit/test/pellets.pcap
Binary files differ
diff --git a/include/dnsjit/test/test-base64url.sh b/include/dnsjit/test/test-base64url.sh
new file mode 100755
index 0000000..163f847
--- /dev/null
+++ b/include/dnsjit/test/test-base64url.sh
@@ -0,0 +1,20 @@
+#!/bin/sh -e
+# Copyright (c) 2020, CZ.NIC, z.s.p.o.
+# 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 "$srcdir/test_base64url.lua"
diff --git a/include/dnsjit/test/test-ipsplit.sh b/include/dnsjit/test/test-ipsplit.sh
new file mode 100755
index 0000000..3d118d6
--- /dev/null
+++ b/include/dnsjit/test/test-ipsplit.sh
@@ -0,0 +1,20 @@
+#!/bin/sh -e
+# Copyright (c) 2020, CZ.NIC, z.s.p.o.
+# 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 "$srcdir/test_ipsplit.lua"
diff --git a/include/dnsjit/test/test-trie.sh b/include/dnsjit/test/test-trie.sh
new file mode 100755
index 0000000..238eea1
--- /dev/null
+++ b/include/dnsjit/test/test-trie.sh
@@ -0,0 +1,20 @@
+#!/bin/sh -e
+# Copyright (c) 2020, CZ.NIC, z.s.p.o.
+# 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 "$srcdir/test_trie.lua"
diff --git a/include/dnsjit/test/test1.gold b/include/dnsjit/test/test1.gold
new file mode 100644
index 0000000..7ded214
--- /dev/null
+++ b/include/dnsjit/test/test1.gold
@@ -0,0 +1,1493 @@
+udp 172.17.0.10:53199 -> 8.8.8.8:53
+id: 59311
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:53199
+id: 59311
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 44 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157880 <12> <56>ns4.<12>
+ IN NS 157880 <12> <74>ns3.<12>
+ IN NS 157880 <12> <92>ns1.<12>
+ IN NS 157880 <12> <110>ns2.<12>
+additionals: class type ttl labels RR labels
+ IN A 157880 <110>
+ IN A 331882 <92>
+ IN A 157880 <74>
+ IN A 157880 <56>
+udp 172.17.0.10:57822 -> 8.8.8.8:53
+id: 35665
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:57822
+id: 35665
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72125 <12> <57>dfw06s47-in-f14.<73>1e100.<79>net.
+ IN PTR 72125 <12> <96>dfw06s47-in-f206.<73>
+authorities: class type ttl labels RR labels
+ IN NS 71608 <16> <127>ns2.<131>google.<138>com.
+ IN NS 71608 <16> <155>ns3.<131>
+ IN NS 71608 <16> <173>ns1.<131>
+ IN NS 71608 <16> <191>ns4.<131>
+additionals: class type ttl labels RR labels
+ IN A 331882 <173>
+ IN A 157880 <155>
+ IN A 157880 <191>
+ IN A 157880 <127>
+udp 172.17.0.10:40043 -> 8.8.8.8:53
+id: 5337
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:40043
+id: 5337
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 44 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157880 <12> <56>ns1.<12>
+ IN NS 157880 <12> <74>ns2.<12>
+ IN NS 157880 <12> <92>ns3.<12>
+ IN NS 157880 <12> <110>ns4.<12>
+additionals: class type ttl labels RR labels
+ IN A 157880 <74>
+ IN A 331882 <56>
+ IN A 157880 <92>
+ IN A 157880 <110>
+udp 172.17.0.10:37953 -> 8.8.8.8:53
+id: 22982
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:37953
+id: 22982
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 34 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157870 <12> <56>ns4.<12>
+ IN NS 157870 <12> <74>ns1.<12>
+ IN NS 157870 <12> <92>ns2.<12>
+ IN NS 157870 <12> <110>ns3.<12>
+additionals: class type ttl labels RR labels
+ IN A 157870 <92>
+ IN A 331872 <74>
+ IN A 157870 <110>
+ IN A 157870 <56>
+udp 172.17.0.10:48658 -> 8.8.8.8:53
+id: 18718
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:48658
+id: 18718
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72115 <12> <57>dfw06s47-in-f206.<74>1e100.<80>net.
+ IN PTR 72115 <12> <97>dfw06s47-in-f14.<74>
+authorities: class type ttl labels RR labels
+ IN NS 71598 <16> <127>ns2.<131>google.<138>com.
+ IN NS 71598 <16> <155>ns3.<131>
+ IN NS 71598 <16> <173>ns4.<131>
+ IN NS 71598 <16> <191>ns1.<131>
+additionals: class type ttl labels RR labels
+ IN A 331872 <191>
+ IN A 157870 <155>
+ IN A 157870 <173>
+ IN A 157870 <127>
+udp 172.17.0.10:40953 -> 8.8.8.8:53
+id: 22531
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:40953
+id: 22531
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 297 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157828 <12> <56>ns2.<12>
+ IN NS 157828 <12> <74>ns4.<12>
+ IN NS 157828 <12> <92>ns1.<12>
+ IN NS 157828 <12> <110>ns3.<12>
+additionals: class type ttl labels RR labels
+ IN A 157828 <56>
+ IN A 331830 <92>
+ IN A 157828 <110>
+ IN A 157828 <74>
+udp 172.17.0.10:45174 -> 8.8.8.8:53
+id: 58510
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:45174
+id: 58510
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 291 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157822 <12> <56>ns2.<12>
+ IN NS 157822 <12> <74>ns3.<12>
+ IN NS 157822 <12> <92>ns1.<12>
+ IN NS 157822 <12> <110>ns4.<12>
+additionals: class type ttl labels RR labels
+ IN A 157822 <56>
+ IN A 331824 <92>
+ IN A 157822 <74>
+ IN A 157822 <110>
+udp 172.17.0.10:33916 -> 8.8.8.8:53
+id: 45248
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:33916
+id: 45248
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72067 <12> <57>dfw06s47-in-f14.<73>1e100.<79>net.
+ IN PTR 72067 <12> <96>dfw06s47-in-f206.<73>
+authorities: class type ttl labels RR labels
+ IN NS 71550 <16> <127>ns3.<131>google.<138>com.
+ IN NS 71550 <16> <155>ns4.<131>
+ IN NS 71550 <16> <173>ns2.<131>
+ IN NS 71550 <16> <191>ns1.<131>
+additionals: class type ttl labels RR labels
+ IN A 331824 <191>
+ IN A 157822 <127>
+ IN A 157822 <155>
+ IN A 157822 <173>
+udp 172.17.0.10:43559 -> 8.8.8.8:53
+id: 49483
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:43559
+id: 49483
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 285 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157816 <12> <56>ns4.<12>
+ IN NS 157816 <12> <74>ns3.<12>
+ IN NS 157816 <12> <92>ns1.<12>
+ IN NS 157816 <12> <110>ns2.<12>
+additionals: class type ttl labels RR labels
+ IN A 157816 <110>
+ IN A 331818 <92>
+ IN A 157816 <74>
+ IN A 157816 <56>
+udp 172.17.0.10:54859 -> 8.8.8.8:53
+id: 31669
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:54859
+id: 31669
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 283 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157814 <12> <56>ns2.<12>
+ IN NS 157814 <12> <74>ns1.<12>
+ IN NS 157814 <12> <92>ns4.<12>
+ IN NS 157814 <12> <110>ns3.<12>
+additionals: class type ttl labels RR labels
+ IN A 157814 <56>
+ IN A 331816 <74>
+ IN A 157814 <110>
+ IN A 157814 <92>
+udp 172.17.0.10:58176 -> 8.8.8.8:53
+id: 25433
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:58176
+id: 25433
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72059 <12> <57>dfw06s47-in-f206.<74>1e100.<80>net.
+ IN PTR 72059 <12> <97>dfw06s47-in-f14.<74>
+authorities: class type ttl labels RR labels
+ IN NS 71542 <16> <127>ns4.<131>google.<138>com.
+ IN NS 71542 <16> <155>ns1.<131>
+ IN NS 71542 <16> <173>ns3.<131>
+ IN NS 71542 <16> <191>ns2.<131>
+additionals: class type ttl labels RR labels
+ IN A 331816 <155>
+ IN A 157814 <173>
+ IN A 157814 <127>
+ IN A 157814 <191>
+udp 172.17.0.10:41266 -> 8.8.8.8:53
+id: 63798
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:41266
+id: 63798
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 282 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157813 <12> <56>ns4.<12>
+ IN NS 157813 <12> <74>ns1.<12>
+ IN NS 157813 <12> <92>ns3.<12>
+ IN NS 157813 <12> <110>ns2.<12>
+additionals: class type ttl labels RR labels
+ IN A 157813 <110>
+ IN A 331815 <74>
+ IN A 157813 <92>
+ IN A 157813 <56>
+udp 172.17.0.10:34607 -> 8.8.8.8:53
+id: 8470
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:34607
+id: 8470
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72058 <12> <57>dfw06s47-in-f14.<73>1e100.<79>net.
+ IN PTR 72058 <12> <96>dfw06s47-in-f206.<73>
+authorities: class type ttl labels RR labels
+ IN NS 71541 <16> <127>ns1.<131>google.<138>com.
+ IN NS 71541 <16> <155>ns2.<131>
+ IN NS 71541 <16> <173>ns4.<131>
+ IN NS 71541 <16> <191>ns3.<131>
+additionals: class type ttl labels RR labels
+ IN A 331815 <127>
+ IN A 157813 <191>
+ IN A 157813 <173>
+ IN A 157813 <155>
+udp 172.17.0.10:60437 -> 8.8.8.8:53
+id: 60258
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:60437
+id: 60258
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 281 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157812 <12> <56>ns3.<12>
+ IN NS 157812 <12> <74>ns2.<12>
+ IN NS 157812 <12> <92>ns4.<12>
+ IN NS 157812 <12> <110>ns1.<12>
+additionals: class type ttl labels RR labels
+ IN A 157812 <74>
+ IN A 331814 <110>
+ IN A 157812 <56>
+ IN A 157812 <92>
+udp 172.17.0.10:37149 -> 8.8.8.8:53
+id: 44985
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:37149
+id: 44985
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72057 <12> <57>dfw06s47-in-f206.<74>1e100.<80>net.
+ IN PTR 72057 <12> <97>dfw06s47-in-f14.<74>
+authorities: class type ttl labels RR labels
+ IN NS 71540 <16> <127>ns4.<131>google.<138>com.
+ IN NS 71540 <16> <155>ns3.<131>
+ IN NS 71540 <16> <173>ns1.<131>
+ IN NS 71540 <16> <191>ns2.<131>
+additionals: class type ttl labels RR labels
+ IN A 331814 <173>
+ IN A 157812 <155>
+ IN A 157812 <127>
+ IN A 157812 <191>
+udp 172.17.0.10:53820 -> 8.8.8.8:53
+id: 45512
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:53820
+id: 45512
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 280 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157811 <12> <56>ns3.<12>
+ IN NS 157811 <12> <74>ns4.<12>
+ IN NS 157811 <12> <92>ns1.<12>
+ IN NS 157811 <12> <110>ns2.<12>
+additionals: class type ttl labels RR labels
+ IN A 157811 <110>
+ IN A 331813 <92>
+ IN A 157811 <56>
+ IN A 157811 <74>
+udp 172.17.0.10:52368 -> 8.8.8.8:53
+id: 22980
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:52368
+id: 22980
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72056 <12> <57>dfw06s47-in-f14.<73>1e100.<79>net.
+ IN PTR 72056 <12> <96>dfw06s47-in-f206.<73>
+authorities: class type ttl labels RR labels
+ IN NS 71539 <16> <127>ns2.<131>google.<138>com.
+ IN NS 71539 <16> <155>ns3.<131>
+ IN NS 71539 <16> <173>ns4.<131>
+ IN NS 71539 <16> <191>ns1.<131>
+additionals: class type ttl labels RR labels
+ IN A 331813 <191>
+ IN A 157811 <155>
+ IN A 157811 <173>
+ IN A 157811 <127>
+udp 172.17.0.10:47637 -> 8.8.8.8:53
+id: 1834
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:47637
+id: 1834
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 279 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157810 <12> <56>ns1.<12>
+ IN NS 157810 <12> <74>ns2.<12>
+ IN NS 157810 <12> <92>ns4.<12>
+ IN NS 157810 <12> <110>ns3.<12>
+additionals: class type ttl labels RR labels
+ IN A 157810 <74>
+ IN A 331812 <56>
+ IN A 157810 <110>
+ IN A 157810 <92>
+udp 172.17.0.10:34426 -> 8.8.8.8:53
+id: 25431
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:34426
+id: 25431
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72055 <12> <57>dfw06s47-in-f206.<74>1e100.<80>net.
+ IN PTR 72055 <12> <97>dfw06s47-in-f14.<74>
+authorities: class type ttl labels RR labels
+ IN NS 71538 <16> <127>ns1.<131>google.<138>com.
+ IN NS 71538 <16> <155>ns4.<131>
+ IN NS 71538 <16> <173>ns3.<131>
+ IN NS 71538 <16> <191>ns2.<131>
+additionals: class type ttl labels RR labels
+ IN A 331812 <127>
+ IN A 157810 <173>
+ IN A 157810 <155>
+ IN A 157810 <191>
+udp 172.17.0.10:41059 -> 8.8.8.8:53
+id: 48432
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:41059
+id: 48432
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 278 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157809 <12> <56>ns3.<12>
+ IN NS 157809 <12> <74>ns4.<12>
+ IN NS 157809 <12> <92>ns2.<12>
+ IN NS 157809 <12> <110>ns1.<12>
+additionals: class type ttl labels RR labels
+ IN A 157809 <92>
+ IN A 331811 <110>
+ IN A 157809 <56>
+ IN A 157809 <74>
+udp 172.17.0.10:51181 -> 8.8.8.8:53
+id: 47411
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:51181
+id: 47411
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72054 <12> <57>dfw06s47-in-f14.<73>1e100.<79>net.
+ IN PTR 72054 <12> <96>dfw06s47-in-f206.<73>
+authorities: class type ttl labels RR labels
+ IN NS 71537 <16> <127>ns2.<131>google.<138>com.
+ IN NS 71537 <16> <155>ns1.<131>
+ IN NS 71537 <16> <173>ns3.<131>
+ IN NS 71537 <16> <191>ns4.<131>
+additionals: class type ttl labels RR labels
+ IN A 331811 <155>
+ IN A 157809 <173>
+ IN A 157809 <191>
+ IN A 157809 <127>
+udp 172.17.0.10:32976 -> 8.8.8.8:53
+id: 12038
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:32976
+id: 12038
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 277 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157808 <12> <56>ns2.<12>
+ IN NS 157808 <12> <74>ns3.<12>
+ IN NS 157808 <12> <92>ns1.<12>
+ IN NS 157808 <12> <110>ns4.<12>
+additionals: class type ttl labels RR labels
+ IN A 157808 <56>
+ IN A 331810 <92>
+ IN A 157808 <74>
+ IN A 157808 <110>
+udp 172.17.0.10:53467 -> 8.8.8.8:53
+id: 11614
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:53467
+id: 11614
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 275 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157806 <12> <56>ns3.<12>
+ IN NS 157806 <12> <74>ns1.<12>
+ IN NS 157806 <12> <92>ns4.<12>
+ IN NS 157806 <12> <110>ns2.<12>
+additionals: class type ttl labels RR labels
+ IN A 157806 <110>
+ IN A 331808 <74>
+ IN A 157806 <56>
+ IN A 157806 <92>
+udp 172.17.0.10:41532 -> 8.8.8.8:53
+id: 59173
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:41532
+id: 59173
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 273 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157804 <12> <56>ns1.<12>
+ IN NS 157804 <12> <74>ns3.<12>
+ IN NS 157804 <12> <92>ns2.<12>
+ IN NS 157804 <12> <110>ns4.<12>
+additionals: class type ttl labels RR labels
+ IN A 157804 <92>
+ IN A 331806 <56>
+ IN A 157804 <74>
+ IN A 157804 <110>
+udp 172.17.0.10:44982 -> 8.8.8.8:53
+id: 45535
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:44982
+id: 45535
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 271 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157802 <12> <56>ns4.<12>
+ IN NS 157802 <12> <74>ns2.<12>
+ IN NS 157802 <12> <92>ns1.<12>
+ IN NS 157802 <12> <110>ns3.<12>
+additionals: class type ttl labels RR labels
+ IN A 157802 <74>
+ IN A 331804 <92>
+ IN A 157802 <110>
+ IN A 157802 <56>
+udp 172.17.0.10:40224 -> 8.8.8.8:53
+id: 60808
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:40224
+id: 60808
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72047 <12> <57>dfw06s47-in-f206.<74>1e100.<80>net.
+ IN PTR 72047 <12> <97>dfw06s47-in-f14.<74>
+authorities: class type ttl labels RR labels
+ IN NS 71530 <16> <127>ns3.<131>google.<138>com.
+ IN NS 71530 <16> <155>ns4.<131>
+ IN NS 71530 <16> <173>ns2.<131>
+ IN NS 71530 <16> <191>ns1.<131>
+additionals: class type ttl labels RR labels
+ IN A 331804 <191>
+ IN A 157802 <127>
+ IN A 157802 <155>
+ IN A 157802 <173>
+udp 172.17.0.10:45658 -> 8.8.8.8:53
+id: 64325
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:45658
+id: 64325
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 270 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157801 <12> <56>ns1.<12>
+ IN NS 157801 <12> <74>ns3.<12>
+ IN NS 157801 <12> <92>ns4.<12>
+ IN NS 157801 <12> <110>ns2.<12>
+additionals: class type ttl labels RR labels
+ IN A 157801 <110>
+ IN A 331803 <56>
+ IN A 157801 <74>
+ IN A 157801 <92>
+udp 172.17.0.10:60457 -> 8.8.8.8:53
+id: 25543
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:60457
+id: 25543
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72046 <12> <57>dfw06s47-in-f14.<73>1e100.<79>net.
+ IN PTR 72046 <12> <96>dfw06s47-in-f206.<73>
+authorities: class type ttl labels RR labels
+ IN NS 71529 <16> <127>ns2.<131>google.<138>com.
+ IN NS 71529 <16> <155>ns3.<131>
+ IN NS 71529 <16> <173>ns4.<131>
+ IN NS 71529 <16> <191>ns1.<131>
+additionals: class type ttl labels RR labels
+ IN A 331803 <191>
+ IN A 157801 <155>
+ IN A 157801 <173>
+ IN A 157801 <127>
+udp 172.17.0.10:59762 -> 8.8.8.8:53
+id: 20736
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:59762
+id: 20736
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 269 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157800 <12> <56>ns3.<12>
+ IN NS 157800 <12> <74>ns1.<12>
+ IN NS 157800 <12> <92>ns4.<12>
+ IN NS 157800 <12> <110>ns2.<12>
+additionals: class type ttl labels RR labels
+ IN A 157800 <110>
+ IN A 331802 <74>
+ IN A 157800 <56>
+ IN A 157800 <92>
+udp 172.17.0.10:56022 -> 8.8.8.8:53
+id: 25911
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:56022
+id: 25911
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72045 <12> <57>dfw06s47-in-f206.<74>1e100.<80>net.
+ IN PTR 72045 <12> <97>dfw06s47-in-f14.<74>
+authorities: class type ttl labels RR labels
+ IN NS 71528 <16> <127>ns1.<131>google.<138>com.
+ IN NS 71528 <16> <155>ns4.<131>
+ IN NS 71528 <16> <173>ns2.<131>
+ IN NS 71528 <16> <191>ns3.<131>
+additionals: class type ttl labels RR labels
+ IN A 331802 <127>
+ IN A 157800 <191>
+ IN A 157800 <155>
+ IN A 157800 <173>
+udp 172.17.0.10:37669 -> 8.8.8.8:53
+id: 64358
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:37669
+id: 64358
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 268 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157799 <12> <56>ns2.<12>
+ IN NS 157799 <12> <74>ns1.<12>
+ IN NS 157799 <12> <92>ns4.<12>
+ IN NS 157799 <12> <110>ns3.<12>
+additionals: class type ttl labels RR labels
+ IN A 157799 <56>
+ IN A 331801 <74>
+ IN A 157799 <110>
+ IN A 157799 <92>
+udp 172.17.0.10:42978 -> 8.8.8.8:53
+id: 37698
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:42978
+id: 37698
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72044 <12> <57>dfw06s47-in-f14.<73>1e100.<79>net.
+ IN PTR 72044 <12> <96>dfw06s47-in-f206.<73>
+authorities: class type ttl labels RR labels
+ IN NS 71527 <16> <127>ns1.<131>google.<138>com.
+ IN NS 71527 <16> <155>ns4.<131>
+ IN NS 71527 <16> <173>ns3.<131>
+ IN NS 71527 <16> <191>ns2.<131>
+additionals: class type ttl labels RR labels
+ IN A 331801 <127>
+ IN A 157799 <173>
+ IN A 157799 <155>
+ IN A 157799 <191>
+udp 172.17.0.10:49829 -> 8.8.8.8:53
+id: 54706
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:49829
+id: 54706
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 267 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157798 <12> <56>ns2.<12>
+ IN NS 157798 <12> <74>ns4.<12>
+ IN NS 157798 <12> <92>ns3.<12>
+ IN NS 157798 <12> <110>ns1.<12>
+additionals: class type ttl labels RR labels
+ IN A 157798 <56>
+ IN A 331800 <110>
+ IN A 157798 <92>
+ IN A 157798 <74>
+udp 172.17.0.10:50599 -> 8.8.8.8:53
+id: 32142
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:50599
+id: 32142
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72043 <12> <57>dfw06s47-in-f206.<74>1e100.<80>net.
+ IN PTR 72043 <12> <97>dfw06s47-in-f14.<74>
+authorities: class type ttl labels RR labels
+ IN NS 71526 <16> <127>ns3.<131>google.<138>com.
+ IN NS 71526 <16> <155>ns1.<131>
+ IN NS 71526 <16> <173>ns2.<131>
+ IN NS 71526 <16> <191>ns4.<131>
+additionals: class type ttl labels RR labels
+ IN A 331800 <155>
+ IN A 157798 <127>
+ IN A 157798 <191>
+ IN A 157798 <173>
+udp 172.17.0.10:44980 -> 8.8.8.8:53
+id: 41808
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:44980
+id: 41808
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 266 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157797 <12> <56>ns2.<12>
+ IN NS 157797 <12> <74>ns4.<12>
+ IN NS 157797 <12> <92>ns1.<12>
+ IN NS 157797 <12> <110>ns3.<12>
+additionals: class type ttl labels RR labels
+ IN A 157797 <56>
+ IN A 331799 <92>
+ IN A 157797 <110>
+ IN A 157797 <74>
+udp 172.17.0.10:60063 -> 8.8.8.8:53
+id: 18886
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:60063
+id: 18886
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72042 <12> <57>dfw06s47-in-f14.<73>1e100.<79>net.
+ IN PTR 72042 <12> <96>dfw06s47-in-f206.<73>
+authorities: class type ttl labels RR labels
+ IN NS 71525 <16> <127>ns3.<131>google.<138>com.
+ IN NS 71525 <16> <155>ns1.<131>
+ IN NS 71525 <16> <173>ns4.<131>
+ IN NS 71525 <16> <191>ns2.<131>
+additionals: class type ttl labels RR labels
+ IN A 331799 <155>
+ IN A 157797 <127>
+ IN A 157797 <173>
+ IN A 157797 <191>
+udp 172.17.0.10:42042 -> 8.8.8.8:53
+id: 10624
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:42042
+id: 10624
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 265 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157796 <12> <56>ns3.<12>
+ IN NS 157796 <12> <74>ns4.<12>
+ IN NS 157796 <12> <92>ns1.<12>
+ IN NS 157796 <12> <110>ns2.<12>
+additionals: class type ttl labels RR labels
+ IN A 157796 <110>
+ IN A 331798 <92>
+ IN A 157796 <56>
+ IN A 157796 <74>
+udp 172.17.0.10:60469 -> 8.8.8.8:53
+id: 33139
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:60469
+id: 33139
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72041 <12> <57>dfw06s47-in-f206.<74>1e100.<80>net.
+ IN PTR 72041 <12> <97>dfw06s47-in-f14.<74>
+authorities: class type ttl labels RR labels
+ IN NS 71524 <16> <127>ns2.<131>google.<138>com.
+ IN NS 71524 <16> <155>ns4.<131>
+ IN NS 71524 <16> <173>ns3.<131>
+ IN NS 71524 <16> <191>ns1.<131>
+additionals: class type ttl labels RR labels
+ IN A 331798 <191>
+ IN A 157796 <173>
+ IN A 157796 <155>
+ IN A 157796 <127>
+udp 172.17.0.10:45703 -> 8.8.8.8:53
+id: 61415
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:45703
+id: 61415
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 264 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157795 <12> <56>ns3.<12>
+ IN NS 157795 <12> <74>ns4.<12>
+ IN NS 157795 <12> <92>ns2.<12>
+ IN NS 157795 <12> <110>ns1.<12>
+additionals: class type ttl labels RR labels
+ IN A 157795 <92>
+ IN A 331797 <110>
+ IN A 157795 <56>
+ IN A 157795 <74>
+udp 172.17.0.10:33507 -> 8.8.8.8:53
+id: 59258
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+udp 8.8.8.8:53 -> 172.17.0.10:33507
+id: 59258
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 2
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN PTR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa.
+answers: class type ttl labels RR labels
+ IN PTR 72040 <12> <57>dfw06s47-in-f14.<73>1e100.<79>net.
+ IN PTR 72040 <12> <96>dfw06s47-in-f206.<73>
+authorities: class type ttl labels RR labels
+ IN NS 71523 <16> <127>ns1.<131>google.<138>com.
+ IN NS 71523 <16> <155>ns4.<131>
+ IN NS 71523 <16> <173>ns3.<131>
+ IN NS 71523 <16> <191>ns2.<131>
+additionals: class type ttl labels RR labels
+ IN A 331797 <127>
+ IN A 157795 <173>
+ IN A 157795 <155>
+ IN A 157795 <191>
+udp 172.17.0.10:46798 -> 8.8.8.8:53
+id: 17700
+ qr: 0
+ opcode: QUERY
+ flags: RD
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 0
+ nscount: 0
+ arcount: 0
+questions: class type labels
+ IN A <12>google.<19>com.
+udp 8.8.8.8:53 -> 172.17.0.10:46798
+id: 17700
+ qr: 1
+ opcode: QUERY
+ flags: RD RA
+ rcode: NOERROR
+ qdcount: 1
+ ancount: 1
+ nscount: 4
+ arcount: 4
+questions: class type labels
+ IN A <12>google.<19>com.
+answers: class type ttl labels RR labels
+ IN A 263 <12>
+authorities: class type ttl labels RR labels
+ IN NS 157794 <12> <56>ns1.<12>
+ IN NS 157794 <12> <74>ns4.<12>
+ IN NS 157794 <12> <92>ns3.<12>
+ IN NS 157794 <12> <110>ns2.<12>
+additionals: class type ttl labels RR labels
+ IN A 157794 <110>
+ IN A 331796 <56>
+ IN A 157794 <92>
+ IN A 157794 <74>
diff --git a/include/dnsjit/test/test1.sh b/include/dnsjit/test/test1.sh
new file mode 100755
index 0000000..1af3a21
--- /dev/null
+++ b/include/dnsjit/test/test1.sh
@@ -0,0 +1,31 @@
+#!/bin/sh -e
+# 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 "$srcdir/../../examples/dumpdns.lua" dns.pcap-dist >test1.out
+diff "$srcdir/test1.gold" test1.out
+
+support=`../dnsjit "$srcdir/test_compressupport.lua"`
+if echo "$support"|grep -q lz4; then
+ ../dnsjit "$srcdir/../../examples/dumpdns.lua" dns.pcap.lz4-dist lz4 >test1.out
+ diff "$srcdir/test1.gold" test1.out
+fi
+if echo "$support"|grep -q zstd; then
+ ../dnsjit "$srcdir/../../examples/dumpdns.lua" dns.pcap.zst-dist zstd >test1.out
+ diff "$srcdir/test1.gold" test1.out
+fi
diff --git a/include/dnsjit/test/test2.gold b/include/dnsjit/test/test2.gold
new file mode 100644
index 0000000..2fb62b0
--- /dev/null
+++ b/include/dnsjit/test/test2.gold
@@ -0,0 +1,42 @@
+src dst id rcode qname qtype
+172.17.0.10 8.8.8.8 59311 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 35665 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 5337 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 22982 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 18718 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 22531 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 58510 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 45248 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 49483 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 31669 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 25433 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 63798 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 8470 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 60258 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 44985 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 45512 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 22980 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 1834 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 25431 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 48432 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 47411 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 12038 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 11614 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 59173 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 45535 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 60808 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 64325 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 25543 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 20736 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 25911 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 64358 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 37698 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 54706 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 32142 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 41808 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 18886 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 10624 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 33139 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 61415 NOERROR <12>google.<19>com. A
+172.17.0.10 8.8.8.8 59258 NOERROR <12>206.<16>218.<20>58.<23>216.<27>in-addr.<35>arpa. PTR
+172.17.0.10 8.8.8.8 17700 NOERROR <12>google.<19>com. A
diff --git a/include/dnsjit/test/test2.sh b/include/dnsjit/test/test2.sh
new file mode 100755
index 0000000..9a58d06
--- /dev/null
+++ b/include/dnsjit/test/test2.sh
@@ -0,0 +1,21 @@
+#!/bin/sh -e
+# 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 "$srcdir/../../examples/dumpdns-qr.lua" dns.pcap-dist >test2.out
+diff "$srcdir/test2.gold" test2.out
diff --git a/include/dnsjit/test/test3.gold b/include/dnsjit/test/test3.gold
new file mode 100644
index 0000000..cdbd0bf
--- /dev/null
+++ b/include/dnsjit/test/test3.gold
@@ -0,0 +1,82 @@
+59311 172.17.0.10 -> 8.8.8.8
+59311 8.8.8.8 -> 172.17.0.10
+35665 172.17.0.10 -> 8.8.8.8
+35665 8.8.8.8 -> 172.17.0.10
+5337 172.17.0.10 -> 8.8.8.8
+5337 8.8.8.8 -> 172.17.0.10
+22982 172.17.0.10 -> 8.8.8.8
+22982 8.8.8.8 -> 172.17.0.10
+18718 172.17.0.10 -> 8.8.8.8
+18718 8.8.8.8 -> 172.17.0.10
+22531 172.17.0.10 -> 8.8.8.8
+22531 8.8.8.8 -> 172.17.0.10
+58510 172.17.0.10 -> 8.8.8.8
+58510 8.8.8.8 -> 172.17.0.10
+45248 172.17.0.10 -> 8.8.8.8
+45248 8.8.8.8 -> 172.17.0.10
+49483 172.17.0.10 -> 8.8.8.8
+49483 8.8.8.8 -> 172.17.0.10
+31669 172.17.0.10 -> 8.8.8.8
+31669 8.8.8.8 -> 172.17.0.10
+25433 172.17.0.10 -> 8.8.8.8
+25433 8.8.8.8 -> 172.17.0.10
+63798 172.17.0.10 -> 8.8.8.8
+63798 8.8.8.8 -> 172.17.0.10
+8470 172.17.0.10 -> 8.8.8.8
+8470 8.8.8.8 -> 172.17.0.10
+60258 172.17.0.10 -> 8.8.8.8
+60258 8.8.8.8 -> 172.17.0.10
+44985 172.17.0.10 -> 8.8.8.8
+44985 8.8.8.8 -> 172.17.0.10
+45512 172.17.0.10 -> 8.8.8.8
+45512 8.8.8.8 -> 172.17.0.10
+22980 172.17.0.10 -> 8.8.8.8
+22980 8.8.8.8 -> 172.17.0.10
+1834 172.17.0.10 -> 8.8.8.8
+1834 8.8.8.8 -> 172.17.0.10
+25431 172.17.0.10 -> 8.8.8.8
+25431 8.8.8.8 -> 172.17.0.10
+48432 172.17.0.10 -> 8.8.8.8
+48432 8.8.8.8 -> 172.17.0.10
+47411 172.17.0.10 -> 8.8.8.8
+47411 8.8.8.8 -> 172.17.0.10
+12038 172.17.0.10 -> 8.8.8.8
+12038 8.8.8.8 -> 172.17.0.10
+11614 172.17.0.10 -> 8.8.8.8
+11614 8.8.8.8 -> 172.17.0.10
+59173 172.17.0.10 -> 8.8.8.8
+59173 8.8.8.8 -> 172.17.0.10
+45535 172.17.0.10 -> 8.8.8.8
+45535 8.8.8.8 -> 172.17.0.10
+60808 172.17.0.10 -> 8.8.8.8
+60808 8.8.8.8 -> 172.17.0.10
+64325 172.17.0.10 -> 8.8.8.8
+64325 8.8.8.8 -> 172.17.0.10
+25543 172.17.0.10 -> 8.8.8.8
+25543 8.8.8.8 -> 172.17.0.10
+20736 172.17.0.10 -> 8.8.8.8
+20736 8.8.8.8 -> 172.17.0.10
+25911 172.17.0.10 -> 8.8.8.8
+25911 8.8.8.8 -> 172.17.0.10
+64358 172.17.0.10 -> 8.8.8.8
+64358 8.8.8.8 -> 172.17.0.10
+37698 172.17.0.10 -> 8.8.8.8
+37698 8.8.8.8 -> 172.17.0.10
+54706 172.17.0.10 -> 8.8.8.8
+54706 8.8.8.8 -> 172.17.0.10
+32142 172.17.0.10 -> 8.8.8.8
+32142 8.8.8.8 -> 172.17.0.10
+41808 172.17.0.10 -> 8.8.8.8
+41808 8.8.8.8 -> 172.17.0.10
+18886 172.17.0.10 -> 8.8.8.8
+18886 8.8.8.8 -> 172.17.0.10
+10624 172.17.0.10 -> 8.8.8.8
+10624 8.8.8.8 -> 172.17.0.10
+33139 172.17.0.10 -> 8.8.8.8
+33139 8.8.8.8 -> 172.17.0.10
+61415 172.17.0.10 -> 8.8.8.8
+61415 8.8.8.8 -> 172.17.0.10
+59258 172.17.0.10 -> 8.8.8.8
+59258 8.8.8.8 -> 172.17.0.10
+17700 172.17.0.10 -> 8.8.8.8
+17700 8.8.8.8 -> 172.17.0.10
diff --git a/include/dnsjit/test/test3.sh b/include/dnsjit/test/test3.sh
new file mode 100755
index 0000000..b9a0b64
--- /dev/null
+++ b/include/dnsjit/test/test3.sh
@@ -0,0 +1,22 @@
+#!/bin/sh -e
+# 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 "$srcdir/../../examples/filter_rcode.lua" dns.pcap-dist 0 >test3.out
+../dnsjit "$srcdir/../../examples/filter_rcode.lua" dns.pcap-dist 1 >>test3.out
+diff "$srcdir/test3.gold" test3.out
diff --git a/include/dnsjit/test/test4.gold b/include/dnsjit/test/test4.gold
new file mode 100644
index 0000000..f56e464
--- /dev/null
+++ b/include/dnsjit/test/test4.gold
@@ -0,0 +1,82 @@
+59311
+59311
+35665
+35665
+5337
+5337
+22982
+22982
+18718
+18718
+22531
+22531
+58510
+58510
+45248
+45248
+49483
+49483
+31669
+31669
+25433
+25433
+63798
+63798
+8470
+8470
+60258
+60258
+44985
+44985
+45512
+45512
+22980
+22980
+1834
+1834
+25431
+25431
+48432
+48432
+47411
+47411
+12038
+12038
+11614
+11614
+59173
+59173
+45535
+45535
+60808
+60808
+64325
+64325
+25543
+25543
+20736
+20736
+25911
+25911
+64358
+64358
+37698
+37698
+54706
+54706
+32142
+32142
+41808
+41808
+18886
+18886
+10624
+10624
+33139
+33139
+61415
+61415
+59258
+59258
+17700
+17700
diff --git a/include/dnsjit/test/test4.sh b/include/dnsjit/test/test4.sh
new file mode 100755
index 0000000..b2d060d
--- /dev/null
+++ b/include/dnsjit/test/test4.sh
@@ -0,0 +1,21 @@
+#!/bin/sh -e
+# 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 "$srcdir/../../examples/readme.lua" dns.pcap-dist >test4.out
+diff "$srcdir/test4.gold" test4.out
diff --git a/include/dnsjit/test/test6.sh b/include/dnsjit/test/test6.sh
new file mode 100755
index 0000000..67f8e25
--- /dev/null
+++ b/include/dnsjit/test/test6.sh
@@ -0,0 +1,23 @@
+#!/bin/sh -e
+# 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 "$srcdir/../../examples/dumpdns.lua" "$srcdir/dns.pcap" > test6-dns.out
+../dnsjit "$srcdir/../../examples/dumpdns2pcap.lua" "$srcdir/dns.pcap" test6-pcap.out
+../dnsjit "$srcdir/../../examples/dumpdns.lua" test6-pcap.out > test6-dns2.out
+diff test6-dns.out test6-dns2.out
diff --git a/include/dnsjit/test/test_base64url.lua b/include/dnsjit/test/test_base64url.lua
new file mode 100644
index 0000000..00aea6a
--- /dev/null
+++ b/include/dnsjit/test/test_base64url.lua
@@ -0,0 +1,24 @@
+base64url = require("dnsjit.lib.base64url")
+ffi = require("ffi")
+
+-- empty string works
+assert(base64url.decode(base64url.encode("")) == "")
+
+-- regular string data
+assert(base64url.encode("abcd") == "YWJjZA")
+assert(base64url.decode(base64url.encode("abcd")) == "abcd")
+
+-- invalid base64 data
+base64url.decode("+")
+
+-- check all symbols - arbitrary binary data
+c_array = ffi.new("uint8_t[?]", 256)
+bin_symbols = {}
+for i = 0, 255 do
+ bin_symbols[i + 1] = string.char(i)
+ c_array[i] = i
+end
+bin_str = table.concat(bin_symbols)
+
+assert(base64url.decode(base64url.encode(bin_str)) == bin_str)
+assert(base64url.encode(c_array, 256) == base64url.encode(bin_str))
diff --git a/include/dnsjit/test/test_compressupport.lua b/include/dnsjit/test/test_compressupport.lua
new file mode 100644
index 0000000..9d4502e
--- /dev/null
+++ b/include/dnsjit/test/test_compressupport.lua
@@ -0,0 +1,10 @@
+local zpcap = require("dnsjit.input.zpcap").new()
+
+zpcap:lz4()
+if zpcap:have_support() then
+ print("lz4")
+end
+zpcap:zstd()
+if zpcap:have_support() then
+ print("zstd")
+end
diff --git a/include/dnsjit/test/test_ipsplit.lua b/include/dnsjit/test/test_ipsplit.lua
new file mode 100755
index 0000000..43dc8a0
--- /dev/null
+++ b/include/dnsjit/test/test_ipsplit.lua
@@ -0,0 +1,294 @@
+-- Test cases for dnsjit.filter.ipsplit
+-- Some checks that use ip_pkt() assume little-endian machine and will fail otherwise
+local ffi = require("ffi")
+local object = require("dnsjit.core.objects")
+local dns = require("dnsjit.core.object.dns").new()
+
+local function dns_msgid(obj)
+ local obj = ffi.cast("core_object_t*", obj)
+ assert(obj, "obj is nil")
+ local pl = obj:cast()
+ assert(obj:type() == "payload" and pl.len > 0, "obj doesn't have payload")
+ dns.obj_prev = obj
+ dns:parse_header()
+ return dns.id
+end
+
+local function ip_pkt(obj)
+ local obj = ffi.cast("core_object_t*", obj)
+ assert(obj, "obj is nil")
+ local pl = obj:cast()
+ assert(obj:type() == "payload" and pl.len > 0, "obj doesn't have payload")
+
+ local pkt = obj.obj_prev
+ while pkt ~= nil do
+ if pkt.obj_type == object.IP or pkt.obj_type == object.IP6 then
+ return pkt:cast()
+ end
+ pkt = pkt.obj_prev
+ end
+ assert(pkt, "obj has no ip/ip6 layer")
+end
+
+
+-----------------------------------------------------
+-- pellets.pcap: client detection
+--
+-- All packets have IPv6 layer and are expected to
+-- be sucessfully processed by ipsplit filter.
+-- Clients should be identified from source ip.
+-----------------------------------------------------
+local input = require("dnsjit.input.pcap").new()
+local layer = require("dnsjit.filter.layer").new()
+local copy = require("dnsjit.filter.copy").new()
+local ipsplit = require("dnsjit.filter.ipsplit").new()
+local out1 = require("dnsjit.core.channel").new(256)
+local out2 = require("dnsjit.core.channel").new(256)
+
+input:open_offline("pellets.pcap-dist")
+layer:producer(input)
+ipsplit:receiver(out1)
+ipsplit:receiver(out2)
+ipsplit:overwrite_dst()
+copy:obj_type(object.IP)
+copy:obj_type(object.IP6)
+copy:obj_type(object.PAYLOAD)
+copy:receiver(ipsplit)
+
+local prod, pctx = layer:produce()
+local recv, rctx = copy:receive()
+
+-- Process entire PCAP first, channels are large enough to bufer all packets
+while true do
+ local obj = prod(pctx)
+ if obj == nil then break end
+ recv(rctx, obj)
+end
+out1:close()
+out2:close()
+
+assert(ipsplit:discarded() == 0, "some valid packets have been discarded")
+assert(out1:size() == 47, "out1: some IPv6 packets lost by filter")
+assert(out2:size() == 44, "out2: some IPv6 packets lost by filter")
+
+-- out1: test individual packets
+local i = 0
+while true do
+ local obj = out1:get()
+ if obj == nil then break end
+ i = i + 1
+
+ if i == 1 then
+ assert(dns_msgid(obj) == 0x0a31, "pkt 1: client 1, pkt 1 -> out1")
+ assert(ip_pkt(obj):source() == "2001:0db8:beef:feed:0000:0000:0000:0003")
+ assert(ip_pkt(obj):destination() == "0100:0000:0000:0000:0000:0000:0000:0001")
+ end
+ if i == 2 then
+ assert(dns_msgid(obj) == 0xb3e8, "pkt 3: client 3, pkt 1 -> out1")
+ assert(ip_pkt(obj):source() == "2001:0db8:beef:feed:0000:0000:0000:0005")
+ assert(ip_pkt(obj):destination() == "0200:0000:0000:0000:0000:0000:0000:0001")
+ end
+ if i == 3 then
+ assert(dns_msgid(obj) == 0xb3e9, "pkt 4: client 3, pkt 2 -> out1")
+ assert(ip_pkt(obj):source() == "2001:0db8:beef:feed:0000:0000:0000:0005")
+ assert(ip_pkt(obj):destination() == "0200:0000:0000:0000:0000:0000:0000:0001")
+ end
+ if i == 13 then assert(dns_msgid(obj) == 0x4a05, "pkt 16: client 7, pkt 1 -> out1") end
+ if i == 14 then assert(dns_msgid(obj) == 0x4a06, "pkt 17: client 7, pkt 2 -> out1") end
+end
+
+-- out2: test individual packets
+local i = 0
+while true do
+ local obj = out2:get()
+ if obj == nil then break end
+ i = i + 1
+
+ if i == 1 then
+ assert(dns_msgid(obj) == 0xe6bd, "pkt 2: client 2, pkt 1 -> out2")
+ assert(ip_pkt(obj):source() == "2001:0db8:beef:feed:0000:0000:0000:0004")
+ assert(ip_pkt(obj):destination() == "0100:0000:0000:0000:0000:0000:0000:0001")
+ end
+ if i == 4 then assert(dns_msgid(obj) == 0xabfe, "pkt 18: client 8, pkt 1 -> out2") end
+ if i == 5 then assert(dns_msgid(obj) == 0xabff, "pkt 21: client 8, pkt 2 -> out2") end
+end
+
+
+-----------------------------------------------------
+-- pellets.pcap: weighted ipsplit:sequential()
+--
+-- Test sequential client assignment that respects
+-- weight.
+-----------------------------------------------------
+local input = require("dnsjit.input.pcap").new()
+local layer = require("dnsjit.filter.layer").new()
+local copy = require("dnsjit.filter.copy").new()
+local ipsplit = require("dnsjit.filter.ipsplit").new()
+local out1 = require("dnsjit.core.channel").new(256)
+local out2 = require("dnsjit.core.channel").new(256)
+
+input:open_offline("pellets.pcap-dist")
+layer:producer(input)
+ipsplit:receiver(out1, 3)
+ipsplit:receiver(out2, 2)
+copy:obj_type(object.IP)
+copy:obj_type(object.IP6)
+copy:obj_type(object.PAYLOAD)
+copy:receiver(ipsplit)
+
+local prod, pctx = layer:produce()
+local recv, rctx = copy:receive()
+
+-- Process entire PCAP first, channels are large enough to bufer all packets
+while true do
+ local obj = prod(pctx)
+ if obj == nil then break end
+ recv(rctx, obj)
+end
+out1:close()
+out2:close()
+
+assert(ipsplit:discarded() == 0, "some valid packets have been discarded")
+assert(out1:size() + out2:size() == 91, "some IPv6 packets lost by filter")
+
+-- out1: test individual packets
+local i = 0
+while true do
+ local obj = out1:get()
+ if obj == nil then break end
+ i = i + 1
+
+ if i == 1 then
+ assert(dns_msgid(obj) == 0x0a31, "pkt 1: client 1, pkt 1 -> out1")
+ assert(ip_pkt(obj):source() == "2001:0db8:beef:feed:0000:0000:0000:0003")
+ assert(ip_pkt(obj):destination() == "0000:0000:0000:0000:0000:0000:0000:0001")
+ end
+ if i == 2 then assert(dns_msgid(obj) == 0xe6bd, "pkt 2: client 2, pkt 1 -> out1") end
+ if i == 3 then assert(dns_msgid(obj) == 0xb3e8, "pkt 3: client 3, pkt 1 -> out1") end
+ if i == 4 then assert(dns_msgid(obj) == 0xb3e9, "pkt 4: client 3, pkt 2 -> out1") end
+ if i == 5 then assert(dns_msgid(obj) == 0x0a6f, "pkt 9: client 6, pkt 1 -> out1") end
+end
+
+-- out2: test individual packets
+local i = 0
+while true do
+ local obj = out2:get()
+ if obj == nil then break end
+ i = i + 1
+
+ if i == 1 then assert(dns_msgid(obj) == 0xaac6, "pkt 5: client 4, pkt 1 -> out2") end
+ if i == 2 then assert(dns_msgid(obj) == 0xaea6, "pkt 6: client 5, pkt 1 -> out2") end
+ if i == 3 then assert(dns_msgid(obj) == 0xaea7, "pkt 7: client 5, pkt 2 -> out2") end
+end
+
+-----------------------------------------------------
+-- pellets.pcap: weighted ipsplit:random()
+--
+-- Test sequential client assignment that respects
+-- weight.
+-----------------------------------------------------
+local input = require("dnsjit.input.pcap").new()
+local layer = require("dnsjit.filter.layer").new()
+local copy = require("dnsjit.filter.copy").new()
+local ipsplit = require("dnsjit.filter.ipsplit").new()
+local out1 = require("dnsjit.core.channel").new(256)
+local out2 = require("dnsjit.core.channel").new(256)
+
+input:open_offline("pellets.pcap-dist")
+layer:producer(input)
+ipsplit:receiver(out1, 85)
+ipsplit:receiver(out2, 15)
+ipsplit:random()
+copy:obj_type(object.IP)
+copy:obj_type(object.IP6)
+copy:obj_type(object.PAYLOAD)
+copy:receiver(ipsplit)
+
+local prod, pctx = layer:produce()
+local recv, rctx = copy:receive()
+
+-- Process entire PCAP first, channels are large enough to bufer all packets
+while true do
+ local obj = prod(pctx)
+ if obj == nil then break end
+ recv(rctx, obj)
+end
+out1:close()
+out2:close()
+
+assert(ipsplit:discarded() == 0, "some valid packets have been discarded")
+assert(out1:size() == 81, "out1: some IPv6 packets lost by filter")
+assert(out2:size() == 10, "out2: some IPv6 packets lost by filter")
+
+-- out1: test individual packets
+local i = 0
+while true do
+ local obj = out1:get()
+ if obj == nil then break end
+ i = i + 1
+ if i == 1 then assert(dns_msgid(obj) == 0xe6bd, "pkt 1: client 2, pkt 1 -> out1") end
+ if i == 2 then assert(dns_msgid(obj) == 0xb3e8, "pkt 2: client 3, pkt 1 -> out1") end
+ if i == 3 then assert(dns_msgid(obj) == 0xb3e9, "pkt 3: client 3, pkt 2 -> out1") end
+ if i == 5 then assert(dns_msgid(obj) == 0xaea6, "pkt 4: client 5, pkt 1 -> out1") end
+ if i == 29 then assert(dns_msgid(obj) == 0xaeaf, "pkt 29: client 5, pkt 10 -> out1") end
+end
+
+-- out2: test individual packets
+local i = 0
+while true do
+ local obj = out2:get()
+ if obj == nil then break end
+ i = i + 1
+ if i == 1 then assert(dns_msgid(obj) == 0x0a31, "pkt 1: client 1, pkt 1 -> out2") end
+ if i == 2 then assert(dns_msgid(obj) == 0x0a6f, "pkt 2: client 6, pkt 1 -> out2") end
+ if i == 10 then assert(dns_msgid(obj) == 0x0a70, "pkt 10: client 6, pkt 2 -> out2") end
+end
+
+-----------------------------------------------------
+-- Tests with dns.pcap
+--
+-- Packets use IPv4 and not all packets have IP layer
+-----------------------------------------------------
+local input = require("dnsjit.input.pcap").new()
+local layer = require("dnsjit.filter.layer").new()
+local copy = require("dnsjit.filter.copy").new()
+local ipsplit = require("dnsjit.filter.ipsplit").new()
+local out1 = require("dnsjit.core.channel").new(256)
+local out2 = require("dnsjit.core.channel").new(256)
+
+input:open_offline("dns.pcap-dist")
+layer:producer(input)
+ipsplit:receiver(out1)
+ipsplit:receiver(out2)
+ipsplit:overwrite_src()
+copy:obj_type(object.IP)
+copy:obj_type(object.IP6)
+copy:obj_type(object.PAYLOAD)
+copy:receiver(ipsplit)
+
+local prod, pctx = layer:produce()
+local recv, rctx = copy:receive()
+
+-- Process entire PCAP first, channels are large enough to bufer all packets
+while true do
+ local obj = prod(pctx)
+ if obj == nil then break end
+ recv(rctx, obj)
+end
+out1:close()
+out2:close()
+
+assert(out1:size() + out2:size() == 123, "some IPv4 packets lost by filter")
+
+-- out1: test individual packets
+local i = 0
+while true do
+ local obj = out1:get()
+ if obj == nil then break end
+ i = i + 1
+ if i == 1 then
+ assert(dns_msgid(obj) == 0xe7af)
+ assert(ip_pkt(obj):source() == "1.0.0.0")
+ assert(ip_pkt(obj):destination() == "8.8.8.8")
+ end
+end
diff --git a/include/dnsjit/test/test_trie.lua b/include/dnsjit/test/test_trie.lua
new file mode 100755
index 0000000..ddaccdf
--- /dev/null
+++ b/include/dnsjit/test/test_trie.lua
@@ -0,0 +1,134 @@
+-- Test cases for dnsjit.lib.trie
+
+local function key_compare(node1, node2)
+ local key1, keylen1 = node1:key()
+ local key2, keylen2 = node2:key()
+ if keylen1 ~= keylen2 then return false end
+ for i = 0, keylen1 - 1 do
+ if key1[i] ~= key2[i] then return false end
+ end
+ return true
+end
+
+-----------------------------------------------------
+-- binary-key trie with which stores numbers
+-----------------------------------------------------
+local input = require("dnsjit.input.pcap").new()
+local layer = require("dnsjit.filter.layer").new()
+local object = require("dnsjit.core.objects")
+local ip = require("dnsjit.lib.ip")
+local trie = require("dnsjit.lib.trie").new("uint32_t", true, 16)
+
+input:open_offline("pellets.pcap-dist")
+layer:producer(input)
+
+local prod, pctx = layer:produce()
+
+-- fill trie with values
+while true do
+ local obj = prod(pctx)
+ if obj == nil then break end
+ local pkt = obj:cast_to(object.IP6)
+
+ if pkt ~= nil then
+ -- count number of packets per IP
+ local node = trie:get_ins(pkt.src)
+ node:set(node:get() + 1)
+ end
+end
+
+assert(trie:weight() == 29)
+
+-- test iterator and check values
+local iter = trie:iter()
+local node = iter:node()
+local npkts = 0
+
+local i = 0
+while node ~= nil do
+ i = i + 1
+ local ip6str = ip.tostring(node:key())
+ local val = tonumber(node:get())
+ npkts = npkts + val
+
+ if i == 1 then assert(ip6str == "2001:0db8:beef:feed:0000:0000:0000:0003" and val == 1) end
+ if i == 1 then
+ local first = trie:get_first()
+ assert(key_compare(node, first))
+ assert(node:get() == first:get())
+ end
+ if i == 2 then assert(ip6str == "2001:0db8:beef:feed:0000:0000:0000:0004" and val == 1) end
+ if i == 2 then
+ local second = trie:get_try(node:key())
+ assert(key_compare(node, second))
+ assert(node:get() == second:get())
+ end
+ if i == 5 then assert(ip6str == "2001:0db8:beef:feed:0000:0000:0000:0008" and val == 10) end
+ if i == 29 then assert(ip6str == "2001:0db8:beef:feed:0000:0000:0000:0042" and val == 1) end
+
+ iter:next()
+ node = iter:node()
+end
+
+assert(npkts == 91)
+
+trie:clear()
+assert(trie:weight() == 0)
+
+
+-----------------------------------------------------
+-- string-key trie with which stores objects
+-----------------------------------------------------
+local input = require("dnsjit.input.pcap").new()
+local layer = require("dnsjit.filter.layer").new()
+local object = require("dnsjit.core.objects")
+local trie = require("dnsjit.lib.trie").new("core_object_t*")
+
+input:open_offline("dns.pcap-dist")
+layer:producer(input)
+
+local prod, pctx = layer:produce()
+
+-- fill trie with values
+while true do
+ local obj = prod(pctx)
+ if obj == nil then break end
+ local pkt = obj:cast_to(object.IP)
+
+ if pkt ~= nil then
+ local node = trie:get_ins(pkt:source())
+ local pkt2 = node:get()
+ if val ~= nil then
+ val:free()
+ end
+ node:set(pkt:copy():uncast())
+ end
+end
+
+assert(trie:weight() == 3)
+
+local node
+node = trie:get_first()
+assert(node:key() == "172.17.0.10")
+pkt = node:get():cast()
+assert(pkt:source() == "172.17.0.10")
+assert(pkt.id == 0x538b)
+pkt:free()
+
+node = trie:get_try("8.8.8.8")
+assert(node:key() == "8.8.8.8")
+pkt = node:get():cast()
+assert(pkt:source() == "8.8.8.8")
+pkt:free()
+
+node = trie:get_try("216.58.218.206")
+assert(node:key() == "216.58.218.206")
+pkt = node:get():cast()
+assert(pkt:source() == "216.58.218.206")
+pkt:free()
+
+node = trie:get_try("nonexistent")
+assert(node == nil)
+
+trie:clear()
+assert(trie:weight() == 0)
diff --git a/include/dnsjit/version.h b/include/dnsjit/version.h
new file mode 100644
index 0000000..c53bdf3
--- /dev/null
+++ b/include/dnsjit/version.h
@@ -0,0 +1,28 @@
+/*
+ * 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/>.
+ */
+
+#ifndef __dnsjit_version_h
+#define __dnsjit_version_h
+
+#define DNSJIT_MAJOR_VERSION 1
+#define DNSJIT_MINOR_VERSION 2
+#define DNSJIT_PATCH_VERSION 1
+
+#endif